diff --git a/bundles/pixi.js-legacy/src/index.js b/bundles/pixi.js-legacy/src/index.js index f1598a1..df6b157 100644 --- a/bundles/pixi.js-legacy/src/index.js +++ b/bundles/pixi.js-legacy/src/index.js @@ -1,5 +1,5 @@ import { accessibility, interaction, prepare, extract } from 'pixi.js'; -import { CanvasRenderer, CanvasTinter } from '@pixi/canvas-renderer'; +import { CanvasRenderer, canvasUtils } from '@pixi/canvas-renderer'; import { CanvasMeshRenderer } from '@pixi/canvas-mesh'; import { CanvasGraphicsRenderer } from '@pixi/canvas-graphics'; import { CanvasSpriteRenderer } from '@pixi/canvas-sprite'; @@ -29,5 +29,5 @@ CanvasGraphicsRenderer, CanvasMeshRenderer, CanvasSpriteRenderer, - CanvasTinter, + canvasUtils, }; diff --git a/bundles/pixi.js-legacy/src/index.js b/bundles/pixi.js-legacy/src/index.js index f1598a1..df6b157 100644 --- a/bundles/pixi.js-legacy/src/index.js +++ b/bundles/pixi.js-legacy/src/index.js @@ -1,5 +1,5 @@ import { accessibility, interaction, prepare, extract } from 'pixi.js'; -import { CanvasRenderer, CanvasTinter } from '@pixi/canvas-renderer'; +import { CanvasRenderer, canvasUtils } from '@pixi/canvas-renderer'; import { CanvasMeshRenderer } from '@pixi/canvas-mesh'; import { CanvasGraphicsRenderer } from '@pixi/canvas-graphics'; import { CanvasSpriteRenderer } from '@pixi/canvas-sprite'; @@ -29,5 +29,5 @@ CanvasGraphicsRenderer, CanvasMeshRenderer, CanvasSpriteRenderer, - CanvasTinter, + canvasUtils, }; diff --git a/bundles/pixi.js/src/useDeprecated.js b/bundles/pixi.js/src/useDeprecated.js index 12f00d4..ab88092 100644 --- a/bundles/pixi.js/src/useDeprecated.js +++ b/bundles/pixi.js/src/useDeprecated.js @@ -138,6 +138,34 @@ return PIXI.systems.FilterSystem; }, }, + + /** + * @namespace PIXI.CanvasTinter + * @see PIXI.canvasUtils + * @deprecated since 5.2.0 + */ + CanvasTinter: { + get() + { + deprecation('5.2.0', 'PIXI.CanvasTinter namespace has moved to PIXI.canvasUtils'); + + return PIXI.canvasUtils; + }, + }, + + /** + * @namespace PIXI.GroupD8 + * @see PIXI.groupD8 + * @deprecated since 5.2.0 + */ + GroupD8: { + get() + { + deprecation('5.2.0', 'PIXI.GroupD8 namespace has moved to PIXI.groupD8'); + + return PIXI.groupD8; + }, + }, }); /** diff --git a/bundles/pixi.js-legacy/src/index.js b/bundles/pixi.js-legacy/src/index.js index f1598a1..df6b157 100644 --- a/bundles/pixi.js-legacy/src/index.js +++ b/bundles/pixi.js-legacy/src/index.js @@ -1,5 +1,5 @@ import { accessibility, interaction, prepare, extract } from 'pixi.js'; -import { CanvasRenderer, CanvasTinter } from '@pixi/canvas-renderer'; +import { CanvasRenderer, canvasUtils } from '@pixi/canvas-renderer'; import { CanvasMeshRenderer } from '@pixi/canvas-mesh'; import { CanvasGraphicsRenderer } from '@pixi/canvas-graphics'; import { CanvasSpriteRenderer } from '@pixi/canvas-sprite'; @@ -29,5 +29,5 @@ CanvasGraphicsRenderer, CanvasMeshRenderer, CanvasSpriteRenderer, - CanvasTinter, + canvasUtils, }; diff --git a/bundles/pixi.js/src/useDeprecated.js b/bundles/pixi.js/src/useDeprecated.js index 12f00d4..ab88092 100644 --- a/bundles/pixi.js/src/useDeprecated.js +++ b/bundles/pixi.js/src/useDeprecated.js @@ -138,6 +138,34 @@ return PIXI.systems.FilterSystem; }, }, + + /** + * @namespace PIXI.CanvasTinter + * @see PIXI.canvasUtils + * @deprecated since 5.2.0 + */ + CanvasTinter: { + get() + { + deprecation('5.2.0', 'PIXI.CanvasTinter namespace has moved to PIXI.canvasUtils'); + + return PIXI.canvasUtils; + }, + }, + + /** + * @namespace PIXI.GroupD8 + * @see PIXI.groupD8 + * @deprecated since 5.2.0 + */ + GroupD8: { + get() + { + deprecation('5.2.0', 'PIXI.GroupD8 namespace has moved to PIXI.groupD8'); + + return PIXI.groupD8; + }, + }, }); /** diff --git a/packages/canvas/canvas-mesh/src/NineSlicePlane.js b/packages/canvas/canvas-mesh/src/NineSlicePlane.js index dccfd79..6e2f055 100644 --- a/packages/canvas/canvas-mesh/src/NineSlicePlane.js +++ b/packages/canvas/canvas-mesh/src/NineSlicePlane.js @@ -1,4 +1,4 @@ -import { CanvasTinter } from '@pixi/canvas-renderer'; +import { canvasUtils } from '@pixi/canvas-renderer'; import { NineSlicePlane } from '@pixi/mesh-extras'; /** @@ -50,7 +50,7 @@ this._cachedTint = this.tint; - this._tintedCanvas = CanvasTinter.getTintedCanvas(this, this.tint); + this._tintedCanvas = canvasUtils.getTintedCanvas(this, this.tint); } } diff --git a/bundles/pixi.js-legacy/src/index.js b/bundles/pixi.js-legacy/src/index.js index f1598a1..df6b157 100644 --- a/bundles/pixi.js-legacy/src/index.js +++ b/bundles/pixi.js-legacy/src/index.js @@ -1,5 +1,5 @@ import { accessibility, interaction, prepare, extract } from 'pixi.js'; -import { CanvasRenderer, CanvasTinter } from '@pixi/canvas-renderer'; +import { CanvasRenderer, canvasUtils } from '@pixi/canvas-renderer'; import { CanvasMeshRenderer } from '@pixi/canvas-mesh'; import { CanvasGraphicsRenderer } from '@pixi/canvas-graphics'; import { CanvasSpriteRenderer } from '@pixi/canvas-sprite'; @@ -29,5 +29,5 @@ CanvasGraphicsRenderer, CanvasMeshRenderer, CanvasSpriteRenderer, - CanvasTinter, + canvasUtils, }; diff --git a/bundles/pixi.js/src/useDeprecated.js b/bundles/pixi.js/src/useDeprecated.js index 12f00d4..ab88092 100644 --- a/bundles/pixi.js/src/useDeprecated.js +++ b/bundles/pixi.js/src/useDeprecated.js @@ -138,6 +138,34 @@ return PIXI.systems.FilterSystem; }, }, + + /** + * @namespace PIXI.CanvasTinter + * @see PIXI.canvasUtils + * @deprecated since 5.2.0 + */ + CanvasTinter: { + get() + { + deprecation('5.2.0', 'PIXI.CanvasTinter namespace has moved to PIXI.canvasUtils'); + + return PIXI.canvasUtils; + }, + }, + + /** + * @namespace PIXI.GroupD8 + * @see PIXI.groupD8 + * @deprecated since 5.2.0 + */ + GroupD8: { + get() + { + deprecation('5.2.0', 'PIXI.GroupD8 namespace has moved to PIXI.groupD8'); + + return PIXI.groupD8; + }, + }, }); /** diff --git a/packages/canvas/canvas-mesh/src/NineSlicePlane.js b/packages/canvas/canvas-mesh/src/NineSlicePlane.js index dccfd79..6e2f055 100644 --- a/packages/canvas/canvas-mesh/src/NineSlicePlane.js +++ b/packages/canvas/canvas-mesh/src/NineSlicePlane.js @@ -1,4 +1,4 @@ -import { CanvasTinter } from '@pixi/canvas-renderer'; +import { canvasUtils } from '@pixi/canvas-renderer'; import { NineSlicePlane } from '@pixi/mesh-extras'; /** @@ -50,7 +50,7 @@ this._cachedTint = this.tint; - this._tintedCanvas = CanvasTinter.getTintedCanvas(this, this.tint); + this._tintedCanvas = canvasUtils.getTintedCanvas(this, this.tint); } } diff --git a/packages/canvas/canvas-renderer/src/CanvasTinter.js b/packages/canvas/canvas-renderer/src/CanvasTinter.js deleted file mode 100644 index bfec51a..0000000 --- a/packages/canvas/canvas-renderer/src/CanvasTinter.js +++ /dev/null @@ -1,288 +0,0 @@ -import { hex2rgb, rgb2hex } from '@pixi/utils'; -import { canUseNewCanvasBlendModes } from './utils/canUseNewCanvasBlendModes'; - -/** - * Utility methods for Sprite/Texture tinting. - * - * Tinting with the CanvasRenderer involves creating a new canvas to use as a texture, - * so be aware of the performance implications. - * - * @namespace PIXI.CanvasTinter - * @memberof PIXI - */ -export const CanvasTinter = { - /** - * Basically this method just needs a sprite and a color and tints the sprite with the given color. - * - * @memberof PIXI.CanvasTinter - * @param {PIXI.Sprite} sprite - the sprite to tint - * @param {number} color - the color to use to tint the sprite with - * @return {HTMLCanvasElement} The tinted canvas - */ - getTintedCanvas: (sprite, color) => - { - const texture = sprite.texture; - - color = CanvasTinter.roundColor(color); - - const stringColor = `#${(`00000${(color | 0).toString(16)}`).substr(-6)}`; - - texture.tintCache = texture.tintCache || {}; - - const cachedCanvas = texture.tintCache[stringColor]; - - let canvas; - - if (cachedCanvas) - { - if (cachedCanvas.tintId === texture._updateID) - { - return texture.tintCache[stringColor]; - } - - canvas = texture.tintCache[stringColor]; - } - else - { - canvas = CanvasTinter.canvas || document.createElement('canvas'); - } - - CanvasTinter.tintMethod(texture, color, canvas); - - canvas.tintId = texture._updateID; - - if (CanvasTinter.convertTintToImage) - { - // is this better? - const tintImage = new Image(); - - tintImage.src = canvas.toDataURL(); - - texture.tintCache[stringColor] = tintImage; - } - else - { - texture.tintCache[stringColor] = canvas; - // if we are not converting the texture to an image then we need to lose the reference to the canvas - CanvasTinter.canvas = null; - } - - return canvas; - }, - - /** - * Tint a texture using the 'multiply' operation. - * - * @memberof PIXI.CanvasTinter - * @param {PIXI.Texture} texture - the texture to tint - * @param {number} color - the color to use to tint the sprite with - * @param {HTMLCanvasElement} canvas - the current canvas - */ - tintWithMultiply: (texture, color, canvas) => - { - const context = canvas.getContext('2d'); - const crop = texture._frame.clone(); - const resolution = texture.baseTexture.resolution; - - crop.x *= resolution; - crop.y *= resolution; - crop.width *= resolution; - crop.height *= resolution; - - canvas.width = Math.ceil(crop.width); - canvas.height = Math.ceil(crop.height); - - context.save(); - context.fillStyle = `#${(`00000${(color | 0).toString(16)}`).substr(-6)}`; - - context.fillRect(0, 0, crop.width, crop.height); - - context.globalCompositeOperation = 'multiply'; - - const source = texture.baseTexture.getDrawableSource(); - - context.drawImage( - source, - crop.x, - crop.y, - crop.width, - crop.height, - 0, - 0, - crop.width, - crop.height - ); - - context.globalCompositeOperation = 'destination-atop'; - - context.drawImage( - source, - crop.x, - crop.y, - crop.width, - crop.height, - 0, - 0, - crop.width, - crop.height - ); - context.restore(); - }, - - /** - * Tint a texture using the 'overlay' operation. - * - * @memberof PIXI.CanvasTinter - * @param {PIXI.Texture} texture - the texture to tint - * @param {number} color - the color to use to tint the sprite with - * @param {HTMLCanvasElement} canvas - the current canvas - */ - tintWithOverlay(texture, color, canvas) - { - const context = canvas.getContext('2d'); - const crop = texture._frame.clone(); - const resolution = texture.baseTexture.resolution; - - crop.x *= resolution; - crop.y *= resolution; - crop.width *= resolution; - crop.height *= resolution; - - canvas.width = Math.ceil(crop.width); - canvas.height = Math.ceil(crop.height); - - context.save(); - context.globalCompositeOperation = 'copy'; - context.fillStyle = `#${(`00000${(color | 0).toString(16)}`).substr(-6)}`; - context.fillRect(0, 0, crop.width, crop.height); - - context.globalCompositeOperation = 'destination-atop'; - context.drawImage( - texture.baseTexture.getDrawableSource(), - crop.x, - crop.y, - crop.width, - crop.height, - 0, - 0, - crop.width, - crop.height - ); - - // context.globalCompositeOperation = 'copy'; - context.restore(); - }, - - /** - * Tint a texture pixel per pixel. - * - * @memberof PIXI.CanvasTinter - * @param {PIXI.Texture} texture - the texture to tint - * @param {number} color - the color to use to tint the sprite with - * @param {HTMLCanvasElement} canvas - the current canvas - */ - tintWithPerPixel: (texture, color, canvas) => - { - const context = canvas.getContext('2d'); - const crop = texture._frame.clone(); - const resolution = texture.baseTexture.resolution; - - crop.x *= resolution; - crop.y *= resolution; - crop.width *= resolution; - crop.height *= resolution; - - canvas.width = Math.ceil(crop.width); - canvas.height = Math.ceil(crop.height); - - context.save(); - context.globalCompositeOperation = 'copy'; - context.drawImage( - texture.baseTexture.getDrawableSource(), - crop.x, - crop.y, - crop.width, - crop.height, - 0, - 0, - crop.width, - crop.height - ); - context.restore(); - - const rgbValues = hex2rgb(color); - const r = rgbValues[0]; - const g = rgbValues[1]; - const b = rgbValues[2]; - - const pixelData = context.getImageData(0, 0, crop.width, crop.height); - - const pixels = pixelData.data; - - for (let i = 0; i < pixels.length; i += 4) - { - pixels[i + 0] *= r; - pixels[i + 1] *= g; - pixels[i + 2] *= b; - } - - context.putImageData(pixelData, 0, 0); - }, - - /** - * Rounds the specified color according to the CanvasTinter.cacheStepsPerColorChannel. - * - * @memberof PIXI.CanvasTinter - * @param {number} color - the color to round, should be a hex color - * @return {number} The rounded color. - */ - roundColor: (color) => - { - const step = CanvasTinter.cacheStepsPerColorChannel; - - const rgbValues = hex2rgb(color); - - rgbValues[0] = Math.min(255, (rgbValues[0] / step) * step); - rgbValues[1] = Math.min(255, (rgbValues[1] / step) * step); - rgbValues[2] = Math.min(255, (rgbValues[2] / step) * step); - - return rgb2hex(rgbValues); - }, - - /** - * Number of steps which will be used as a cap when rounding colors. - * - * @memberof PIXI.CanvasTinter - * @type {number} - */ - cacheStepsPerColorChannel: 8, - - /** - * Tint cache boolean flag. - * - * @memberof PIXI.CanvasTinter - * @type {boolean} - */ - convertTintToImage: false, - - /** - * Whether or not the Canvas BlendModes are supported, consequently the ability to tint using the multiply method. - * - * @memberof PIXI.CanvasTinter - * @type {boolean} - */ - canUseMultiply: canUseNewCanvasBlendModes(), - - /** - * The tinting method that will be used. - * - * @memberof PIXI.CanvasTinter - * @type {Function} - */ - tintMethod: () => - { // jslint-disable no-empty-function - - }, -}; - -CanvasTinter.tintMethod = CanvasTinter.canUseMultiply ? CanvasTinter.tintWithMultiply : CanvasTinter.tintWithPerPixel; diff --git a/bundles/pixi.js-legacy/src/index.js b/bundles/pixi.js-legacy/src/index.js index f1598a1..df6b157 100644 --- a/bundles/pixi.js-legacy/src/index.js +++ b/bundles/pixi.js-legacy/src/index.js @@ -1,5 +1,5 @@ import { accessibility, interaction, prepare, extract } from 'pixi.js'; -import { CanvasRenderer, CanvasTinter } from '@pixi/canvas-renderer'; +import { CanvasRenderer, canvasUtils } from '@pixi/canvas-renderer'; import { CanvasMeshRenderer } from '@pixi/canvas-mesh'; import { CanvasGraphicsRenderer } from '@pixi/canvas-graphics'; import { CanvasSpriteRenderer } from '@pixi/canvas-sprite'; @@ -29,5 +29,5 @@ CanvasGraphicsRenderer, CanvasMeshRenderer, CanvasSpriteRenderer, - CanvasTinter, + canvasUtils, }; diff --git a/bundles/pixi.js/src/useDeprecated.js b/bundles/pixi.js/src/useDeprecated.js index 12f00d4..ab88092 100644 --- a/bundles/pixi.js/src/useDeprecated.js +++ b/bundles/pixi.js/src/useDeprecated.js @@ -138,6 +138,34 @@ return PIXI.systems.FilterSystem; }, }, + + /** + * @namespace PIXI.CanvasTinter + * @see PIXI.canvasUtils + * @deprecated since 5.2.0 + */ + CanvasTinter: { + get() + { + deprecation('5.2.0', 'PIXI.CanvasTinter namespace has moved to PIXI.canvasUtils'); + + return PIXI.canvasUtils; + }, + }, + + /** + * @namespace PIXI.GroupD8 + * @see PIXI.groupD8 + * @deprecated since 5.2.0 + */ + GroupD8: { + get() + { + deprecation('5.2.0', 'PIXI.GroupD8 namespace has moved to PIXI.groupD8'); + + return PIXI.groupD8; + }, + }, }); /** diff --git a/packages/canvas/canvas-mesh/src/NineSlicePlane.js b/packages/canvas/canvas-mesh/src/NineSlicePlane.js index dccfd79..6e2f055 100644 --- a/packages/canvas/canvas-mesh/src/NineSlicePlane.js +++ b/packages/canvas/canvas-mesh/src/NineSlicePlane.js @@ -1,4 +1,4 @@ -import { CanvasTinter } from '@pixi/canvas-renderer'; +import { canvasUtils } from '@pixi/canvas-renderer'; import { NineSlicePlane } from '@pixi/mesh-extras'; /** @@ -50,7 +50,7 @@ this._cachedTint = this.tint; - this._tintedCanvas = CanvasTinter.getTintedCanvas(this, this.tint); + this._tintedCanvas = canvasUtils.getTintedCanvas(this, this.tint); } } diff --git a/packages/canvas/canvas-renderer/src/CanvasTinter.js b/packages/canvas/canvas-renderer/src/CanvasTinter.js deleted file mode 100644 index bfec51a..0000000 --- a/packages/canvas/canvas-renderer/src/CanvasTinter.js +++ /dev/null @@ -1,288 +0,0 @@ -import { hex2rgb, rgb2hex } from '@pixi/utils'; -import { canUseNewCanvasBlendModes } from './utils/canUseNewCanvasBlendModes'; - -/** - * Utility methods for Sprite/Texture tinting. - * - * Tinting with the CanvasRenderer involves creating a new canvas to use as a texture, - * so be aware of the performance implications. - * - * @namespace PIXI.CanvasTinter - * @memberof PIXI - */ -export const CanvasTinter = { - /** - * Basically this method just needs a sprite and a color and tints the sprite with the given color. - * - * @memberof PIXI.CanvasTinter - * @param {PIXI.Sprite} sprite - the sprite to tint - * @param {number} color - the color to use to tint the sprite with - * @return {HTMLCanvasElement} The tinted canvas - */ - getTintedCanvas: (sprite, color) => - { - const texture = sprite.texture; - - color = CanvasTinter.roundColor(color); - - const stringColor = `#${(`00000${(color | 0).toString(16)}`).substr(-6)}`; - - texture.tintCache = texture.tintCache || {}; - - const cachedCanvas = texture.tintCache[stringColor]; - - let canvas; - - if (cachedCanvas) - { - if (cachedCanvas.tintId === texture._updateID) - { - return texture.tintCache[stringColor]; - } - - canvas = texture.tintCache[stringColor]; - } - else - { - canvas = CanvasTinter.canvas || document.createElement('canvas'); - } - - CanvasTinter.tintMethod(texture, color, canvas); - - canvas.tintId = texture._updateID; - - if (CanvasTinter.convertTintToImage) - { - // is this better? - const tintImage = new Image(); - - tintImage.src = canvas.toDataURL(); - - texture.tintCache[stringColor] = tintImage; - } - else - { - texture.tintCache[stringColor] = canvas; - // if we are not converting the texture to an image then we need to lose the reference to the canvas - CanvasTinter.canvas = null; - } - - return canvas; - }, - - /** - * Tint a texture using the 'multiply' operation. - * - * @memberof PIXI.CanvasTinter - * @param {PIXI.Texture} texture - the texture to tint - * @param {number} color - the color to use to tint the sprite with - * @param {HTMLCanvasElement} canvas - the current canvas - */ - tintWithMultiply: (texture, color, canvas) => - { - const context = canvas.getContext('2d'); - const crop = texture._frame.clone(); - const resolution = texture.baseTexture.resolution; - - crop.x *= resolution; - crop.y *= resolution; - crop.width *= resolution; - crop.height *= resolution; - - canvas.width = Math.ceil(crop.width); - canvas.height = Math.ceil(crop.height); - - context.save(); - context.fillStyle = `#${(`00000${(color | 0).toString(16)}`).substr(-6)}`; - - context.fillRect(0, 0, crop.width, crop.height); - - context.globalCompositeOperation = 'multiply'; - - const source = texture.baseTexture.getDrawableSource(); - - context.drawImage( - source, - crop.x, - crop.y, - crop.width, - crop.height, - 0, - 0, - crop.width, - crop.height - ); - - context.globalCompositeOperation = 'destination-atop'; - - context.drawImage( - source, - crop.x, - crop.y, - crop.width, - crop.height, - 0, - 0, - crop.width, - crop.height - ); - context.restore(); - }, - - /** - * Tint a texture using the 'overlay' operation. - * - * @memberof PIXI.CanvasTinter - * @param {PIXI.Texture} texture - the texture to tint - * @param {number} color - the color to use to tint the sprite with - * @param {HTMLCanvasElement} canvas - the current canvas - */ - tintWithOverlay(texture, color, canvas) - { - const context = canvas.getContext('2d'); - const crop = texture._frame.clone(); - const resolution = texture.baseTexture.resolution; - - crop.x *= resolution; - crop.y *= resolution; - crop.width *= resolution; - crop.height *= resolution; - - canvas.width = Math.ceil(crop.width); - canvas.height = Math.ceil(crop.height); - - context.save(); - context.globalCompositeOperation = 'copy'; - context.fillStyle = `#${(`00000${(color | 0).toString(16)}`).substr(-6)}`; - context.fillRect(0, 0, crop.width, crop.height); - - context.globalCompositeOperation = 'destination-atop'; - context.drawImage( - texture.baseTexture.getDrawableSource(), - crop.x, - crop.y, - crop.width, - crop.height, - 0, - 0, - crop.width, - crop.height - ); - - // context.globalCompositeOperation = 'copy'; - context.restore(); - }, - - /** - * Tint a texture pixel per pixel. - * - * @memberof PIXI.CanvasTinter - * @param {PIXI.Texture} texture - the texture to tint - * @param {number} color - the color to use to tint the sprite with - * @param {HTMLCanvasElement} canvas - the current canvas - */ - tintWithPerPixel: (texture, color, canvas) => - { - const context = canvas.getContext('2d'); - const crop = texture._frame.clone(); - const resolution = texture.baseTexture.resolution; - - crop.x *= resolution; - crop.y *= resolution; - crop.width *= resolution; - crop.height *= resolution; - - canvas.width = Math.ceil(crop.width); - canvas.height = Math.ceil(crop.height); - - context.save(); - context.globalCompositeOperation = 'copy'; - context.drawImage( - texture.baseTexture.getDrawableSource(), - crop.x, - crop.y, - crop.width, - crop.height, - 0, - 0, - crop.width, - crop.height - ); - context.restore(); - - const rgbValues = hex2rgb(color); - const r = rgbValues[0]; - const g = rgbValues[1]; - const b = rgbValues[2]; - - const pixelData = context.getImageData(0, 0, crop.width, crop.height); - - const pixels = pixelData.data; - - for (let i = 0; i < pixels.length; i += 4) - { - pixels[i + 0] *= r; - pixels[i + 1] *= g; - pixels[i + 2] *= b; - } - - context.putImageData(pixelData, 0, 0); - }, - - /** - * Rounds the specified color according to the CanvasTinter.cacheStepsPerColorChannel. - * - * @memberof PIXI.CanvasTinter - * @param {number} color - the color to round, should be a hex color - * @return {number} The rounded color. - */ - roundColor: (color) => - { - const step = CanvasTinter.cacheStepsPerColorChannel; - - const rgbValues = hex2rgb(color); - - rgbValues[0] = Math.min(255, (rgbValues[0] / step) * step); - rgbValues[1] = Math.min(255, (rgbValues[1] / step) * step); - rgbValues[2] = Math.min(255, (rgbValues[2] / step) * step); - - return rgb2hex(rgbValues); - }, - - /** - * Number of steps which will be used as a cap when rounding colors. - * - * @memberof PIXI.CanvasTinter - * @type {number} - */ - cacheStepsPerColorChannel: 8, - - /** - * Tint cache boolean flag. - * - * @memberof PIXI.CanvasTinter - * @type {boolean} - */ - convertTintToImage: false, - - /** - * Whether or not the Canvas BlendModes are supported, consequently the ability to tint using the multiply method. - * - * @memberof PIXI.CanvasTinter - * @type {boolean} - */ - canUseMultiply: canUseNewCanvasBlendModes(), - - /** - * The tinting method that will be used. - * - * @memberof PIXI.CanvasTinter - * @type {Function} - */ - tintMethod: () => - { // jslint-disable no-empty-function - - }, -}; - -CanvasTinter.tintMethod = CanvasTinter.canUseMultiply ? CanvasTinter.tintWithMultiply : CanvasTinter.tintWithPerPixel; diff --git a/packages/canvas/canvas-renderer/src/canvasUtils.js b/packages/canvas/canvas-renderer/src/canvasUtils.js new file mode 100644 index 0000000..62e16d4 --- /dev/null +++ b/packages/canvas/canvas-renderer/src/canvasUtils.js @@ -0,0 +1,288 @@ +import { hex2rgb, rgb2hex } from '@pixi/utils'; +import { canUseNewCanvasBlendModes } from './utils/canUseNewCanvasBlendModes'; + +/** + * Utility methods for Sprite/Texture tinting. + * + * Tinting with the CanvasRenderer involves creating a new canvas to use as a texture, + * so be aware of the performance implications. + * + * @namespace PIXI.canvasUtils + * @memberof PIXI + */ +export const canvasUtils = { + /** + * Basically this method just needs a sprite and a color and tints the sprite with the given color. + * + * @memberof PIXI.canvasUtils + * @param {PIXI.Sprite} sprite - the sprite to tint + * @param {number} color - the color to use to tint the sprite with + * @return {HTMLCanvasElement} The tinted canvas + */ + getTintedCanvas: (sprite, color) => + { + const texture = sprite.texture; + + color = canvasUtils.roundColor(color); + + const stringColor = `#${(`00000${(color | 0).toString(16)}`).substr(-6)}`; + + texture.tintCache = texture.tintCache || {}; + + const cachedCanvas = texture.tintCache[stringColor]; + + let canvas; + + if (cachedCanvas) + { + if (cachedCanvas.tintId === texture._updateID) + { + return texture.tintCache[stringColor]; + } + + canvas = texture.tintCache[stringColor]; + } + else + { + canvas = canvasUtils.canvas || document.createElement('canvas'); + } + + canvasUtils.tintMethod(texture, color, canvas); + + canvas.tintId = texture._updateID; + + if (canvasUtils.convertTintToImage) + { + // is this better? + const tintImage = new Image(); + + tintImage.src = canvas.toDataURL(); + + texture.tintCache[stringColor] = tintImage; + } + else + { + texture.tintCache[stringColor] = canvas; + // if we are not converting the texture to an image then we need to lose the reference to the canvas + canvasUtils.canvas = null; + } + + return canvas; + }, + + /** + * Tint a texture using the 'multiply' operation. + * + * @memberof PIXI.canvasUtils + * @param {PIXI.Texture} texture - the texture to tint + * @param {number} color - the color to use to tint the sprite with + * @param {HTMLCanvasElement} canvas - the current canvas + */ + tintWithMultiply: (texture, color, canvas) => + { + const context = canvas.getContext('2d'); + const crop = texture._frame.clone(); + const resolution = texture.baseTexture.resolution; + + crop.x *= resolution; + crop.y *= resolution; + crop.width *= resolution; + crop.height *= resolution; + + canvas.width = Math.ceil(crop.width); + canvas.height = Math.ceil(crop.height); + + context.save(); + context.fillStyle = `#${(`00000${(color | 0).toString(16)}`).substr(-6)}`; + + context.fillRect(0, 0, crop.width, crop.height); + + context.globalCompositeOperation = 'multiply'; + + const source = texture.baseTexture.getDrawableSource(); + + context.drawImage( + source, + crop.x, + crop.y, + crop.width, + crop.height, + 0, + 0, + crop.width, + crop.height + ); + + context.globalCompositeOperation = 'destination-atop'; + + context.drawImage( + source, + crop.x, + crop.y, + crop.width, + crop.height, + 0, + 0, + crop.width, + crop.height + ); + context.restore(); + }, + + /** + * Tint a texture using the 'overlay' operation. + * + * @memberof PIXI.canvasUtils + * @param {PIXI.Texture} texture - the texture to tint + * @param {number} color - the color to use to tint the sprite with + * @param {HTMLCanvasElement} canvas - the current canvas + */ + tintWithOverlay(texture, color, canvas) + { + const context = canvas.getContext('2d'); + const crop = texture._frame.clone(); + const resolution = texture.baseTexture.resolution; + + crop.x *= resolution; + crop.y *= resolution; + crop.width *= resolution; + crop.height *= resolution; + + canvas.width = Math.ceil(crop.width); + canvas.height = Math.ceil(crop.height); + + context.save(); + context.globalCompositeOperation = 'copy'; + context.fillStyle = `#${(`00000${(color | 0).toString(16)}`).substr(-6)}`; + context.fillRect(0, 0, crop.width, crop.height); + + context.globalCompositeOperation = 'destination-atop'; + context.drawImage( + texture.baseTexture.getDrawableSource(), + crop.x, + crop.y, + crop.width, + crop.height, + 0, + 0, + crop.width, + crop.height + ); + + // context.globalCompositeOperation = 'copy'; + context.restore(); + }, + + /** + * Tint a texture pixel per pixel. + * + * @memberof PIXI.canvasUtils + * @param {PIXI.Texture} texture - the texture to tint + * @param {number} color - the color to use to tint the sprite with + * @param {HTMLCanvasElement} canvas - the current canvas + */ + tintWithPerPixel: (texture, color, canvas) => + { + const context = canvas.getContext('2d'); + const crop = texture._frame.clone(); + const resolution = texture.baseTexture.resolution; + + crop.x *= resolution; + crop.y *= resolution; + crop.width *= resolution; + crop.height *= resolution; + + canvas.width = Math.ceil(crop.width); + canvas.height = Math.ceil(crop.height); + + context.save(); + context.globalCompositeOperation = 'copy'; + context.drawImage( + texture.baseTexture.getDrawableSource(), + crop.x, + crop.y, + crop.width, + crop.height, + 0, + 0, + crop.width, + crop.height + ); + context.restore(); + + const rgbValues = hex2rgb(color); + const r = rgbValues[0]; + const g = rgbValues[1]; + const b = rgbValues[2]; + + const pixelData = context.getImageData(0, 0, crop.width, crop.height); + + const pixels = pixelData.data; + + for (let i = 0; i < pixels.length; i += 4) + { + pixels[i + 0] *= r; + pixels[i + 1] *= g; + pixels[i + 2] *= b; + } + + context.putImageData(pixelData, 0, 0); + }, + + /** + * Rounds the specified color according to the canvasUtils.cacheStepsPerColorChannel. + * + * @memberof PIXI.canvasUtils + * @param {number} color - the color to round, should be a hex color + * @return {number} The rounded color. + */ + roundColor: (color) => + { + const step = canvasUtils.cacheStepsPerColorChannel; + + const rgbValues = hex2rgb(color); + + rgbValues[0] = Math.min(255, (rgbValues[0] / step) * step); + rgbValues[1] = Math.min(255, (rgbValues[1] / step) * step); + rgbValues[2] = Math.min(255, (rgbValues[2] / step) * step); + + return rgb2hex(rgbValues); + }, + + /** + * Number of steps which will be used as a cap when rounding colors. + * + * @memberof PIXI.canvasUtils + * @type {number} + */ + cacheStepsPerColorChannel: 8, + + /** + * Tint cache boolean flag. + * + * @memberof PIXI.canvasUtils + * @type {boolean} + */ + convertTintToImage: false, + + /** + * Whether or not the Canvas BlendModes are supported, consequently the ability to tint using the multiply method. + * + * @memberof PIXI.canvasUtils + * @type {boolean} + */ + canUseMultiply: canUseNewCanvasBlendModes(), + + /** + * The tinting method that will be used. + * + * @memberof PIXI.canvasUtils + * @type {Function} + */ + tintMethod: () => + { // jslint-disable no-empty-function + + }, +}; + +canvasUtils.tintMethod = canvasUtils.canUseMultiply ? canvasUtils.tintWithMultiply : canvasUtils.tintWithPerPixel; diff --git a/bundles/pixi.js-legacy/src/index.js b/bundles/pixi.js-legacy/src/index.js index f1598a1..df6b157 100644 --- a/bundles/pixi.js-legacy/src/index.js +++ b/bundles/pixi.js-legacy/src/index.js @@ -1,5 +1,5 @@ import { accessibility, interaction, prepare, extract } from 'pixi.js'; -import { CanvasRenderer, CanvasTinter } from '@pixi/canvas-renderer'; +import { CanvasRenderer, canvasUtils } from '@pixi/canvas-renderer'; import { CanvasMeshRenderer } from '@pixi/canvas-mesh'; import { CanvasGraphicsRenderer } from '@pixi/canvas-graphics'; import { CanvasSpriteRenderer } from '@pixi/canvas-sprite'; @@ -29,5 +29,5 @@ CanvasGraphicsRenderer, CanvasMeshRenderer, CanvasSpriteRenderer, - CanvasTinter, + canvasUtils, }; diff --git a/bundles/pixi.js/src/useDeprecated.js b/bundles/pixi.js/src/useDeprecated.js index 12f00d4..ab88092 100644 --- a/bundles/pixi.js/src/useDeprecated.js +++ b/bundles/pixi.js/src/useDeprecated.js @@ -138,6 +138,34 @@ return PIXI.systems.FilterSystem; }, }, + + /** + * @namespace PIXI.CanvasTinter + * @see PIXI.canvasUtils + * @deprecated since 5.2.0 + */ + CanvasTinter: { + get() + { + deprecation('5.2.0', 'PIXI.CanvasTinter namespace has moved to PIXI.canvasUtils'); + + return PIXI.canvasUtils; + }, + }, + + /** + * @namespace PIXI.GroupD8 + * @see PIXI.groupD8 + * @deprecated since 5.2.0 + */ + GroupD8: { + get() + { + deprecation('5.2.0', 'PIXI.GroupD8 namespace has moved to PIXI.groupD8'); + + return PIXI.groupD8; + }, + }, }); /** diff --git a/packages/canvas/canvas-mesh/src/NineSlicePlane.js b/packages/canvas/canvas-mesh/src/NineSlicePlane.js index dccfd79..6e2f055 100644 --- a/packages/canvas/canvas-mesh/src/NineSlicePlane.js +++ b/packages/canvas/canvas-mesh/src/NineSlicePlane.js @@ -1,4 +1,4 @@ -import { CanvasTinter } from '@pixi/canvas-renderer'; +import { canvasUtils } from '@pixi/canvas-renderer'; import { NineSlicePlane } from '@pixi/mesh-extras'; /** @@ -50,7 +50,7 @@ this._cachedTint = this.tint; - this._tintedCanvas = CanvasTinter.getTintedCanvas(this, this.tint); + this._tintedCanvas = canvasUtils.getTintedCanvas(this, this.tint); } } diff --git a/packages/canvas/canvas-renderer/src/CanvasTinter.js b/packages/canvas/canvas-renderer/src/CanvasTinter.js deleted file mode 100644 index bfec51a..0000000 --- a/packages/canvas/canvas-renderer/src/CanvasTinter.js +++ /dev/null @@ -1,288 +0,0 @@ -import { hex2rgb, rgb2hex } from '@pixi/utils'; -import { canUseNewCanvasBlendModes } from './utils/canUseNewCanvasBlendModes'; - -/** - * Utility methods for Sprite/Texture tinting. - * - * Tinting with the CanvasRenderer involves creating a new canvas to use as a texture, - * so be aware of the performance implications. - * - * @namespace PIXI.CanvasTinter - * @memberof PIXI - */ -export const CanvasTinter = { - /** - * Basically this method just needs a sprite and a color and tints the sprite with the given color. - * - * @memberof PIXI.CanvasTinter - * @param {PIXI.Sprite} sprite - the sprite to tint - * @param {number} color - the color to use to tint the sprite with - * @return {HTMLCanvasElement} The tinted canvas - */ - getTintedCanvas: (sprite, color) => - { - const texture = sprite.texture; - - color = CanvasTinter.roundColor(color); - - const stringColor = `#${(`00000${(color | 0).toString(16)}`).substr(-6)}`; - - texture.tintCache = texture.tintCache || {}; - - const cachedCanvas = texture.tintCache[stringColor]; - - let canvas; - - if (cachedCanvas) - { - if (cachedCanvas.tintId === texture._updateID) - { - return texture.tintCache[stringColor]; - } - - canvas = texture.tintCache[stringColor]; - } - else - { - canvas = CanvasTinter.canvas || document.createElement('canvas'); - } - - CanvasTinter.tintMethod(texture, color, canvas); - - canvas.tintId = texture._updateID; - - if (CanvasTinter.convertTintToImage) - { - // is this better? - const tintImage = new Image(); - - tintImage.src = canvas.toDataURL(); - - texture.tintCache[stringColor] = tintImage; - } - else - { - texture.tintCache[stringColor] = canvas; - // if we are not converting the texture to an image then we need to lose the reference to the canvas - CanvasTinter.canvas = null; - } - - return canvas; - }, - - /** - * Tint a texture using the 'multiply' operation. - * - * @memberof PIXI.CanvasTinter - * @param {PIXI.Texture} texture - the texture to tint - * @param {number} color - the color to use to tint the sprite with - * @param {HTMLCanvasElement} canvas - the current canvas - */ - tintWithMultiply: (texture, color, canvas) => - { - const context = canvas.getContext('2d'); - const crop = texture._frame.clone(); - const resolution = texture.baseTexture.resolution; - - crop.x *= resolution; - crop.y *= resolution; - crop.width *= resolution; - crop.height *= resolution; - - canvas.width = Math.ceil(crop.width); - canvas.height = Math.ceil(crop.height); - - context.save(); - context.fillStyle = `#${(`00000${(color | 0).toString(16)}`).substr(-6)}`; - - context.fillRect(0, 0, crop.width, crop.height); - - context.globalCompositeOperation = 'multiply'; - - const source = texture.baseTexture.getDrawableSource(); - - context.drawImage( - source, - crop.x, - crop.y, - crop.width, - crop.height, - 0, - 0, - crop.width, - crop.height - ); - - context.globalCompositeOperation = 'destination-atop'; - - context.drawImage( - source, - crop.x, - crop.y, - crop.width, - crop.height, - 0, - 0, - crop.width, - crop.height - ); - context.restore(); - }, - - /** - * Tint a texture using the 'overlay' operation. - * - * @memberof PIXI.CanvasTinter - * @param {PIXI.Texture} texture - the texture to tint - * @param {number} color - the color to use to tint the sprite with - * @param {HTMLCanvasElement} canvas - the current canvas - */ - tintWithOverlay(texture, color, canvas) - { - const context = canvas.getContext('2d'); - const crop = texture._frame.clone(); - const resolution = texture.baseTexture.resolution; - - crop.x *= resolution; - crop.y *= resolution; - crop.width *= resolution; - crop.height *= resolution; - - canvas.width = Math.ceil(crop.width); - canvas.height = Math.ceil(crop.height); - - context.save(); - context.globalCompositeOperation = 'copy'; - context.fillStyle = `#${(`00000${(color | 0).toString(16)}`).substr(-6)}`; - context.fillRect(0, 0, crop.width, crop.height); - - context.globalCompositeOperation = 'destination-atop'; - context.drawImage( - texture.baseTexture.getDrawableSource(), - crop.x, - crop.y, - crop.width, - crop.height, - 0, - 0, - crop.width, - crop.height - ); - - // context.globalCompositeOperation = 'copy'; - context.restore(); - }, - - /** - * Tint a texture pixel per pixel. - * - * @memberof PIXI.CanvasTinter - * @param {PIXI.Texture} texture - the texture to tint - * @param {number} color - the color to use to tint the sprite with - * @param {HTMLCanvasElement} canvas - the current canvas - */ - tintWithPerPixel: (texture, color, canvas) => - { - const context = canvas.getContext('2d'); - const crop = texture._frame.clone(); - const resolution = texture.baseTexture.resolution; - - crop.x *= resolution; - crop.y *= resolution; - crop.width *= resolution; - crop.height *= resolution; - - canvas.width = Math.ceil(crop.width); - canvas.height = Math.ceil(crop.height); - - context.save(); - context.globalCompositeOperation = 'copy'; - context.drawImage( - texture.baseTexture.getDrawableSource(), - crop.x, - crop.y, - crop.width, - crop.height, - 0, - 0, - crop.width, - crop.height - ); - context.restore(); - - const rgbValues = hex2rgb(color); - const r = rgbValues[0]; - const g = rgbValues[1]; - const b = rgbValues[2]; - - const pixelData = context.getImageData(0, 0, crop.width, crop.height); - - const pixels = pixelData.data; - - for (let i = 0; i < pixels.length; i += 4) - { - pixels[i + 0] *= r; - pixels[i + 1] *= g; - pixels[i + 2] *= b; - } - - context.putImageData(pixelData, 0, 0); - }, - - /** - * Rounds the specified color according to the CanvasTinter.cacheStepsPerColorChannel. - * - * @memberof PIXI.CanvasTinter - * @param {number} color - the color to round, should be a hex color - * @return {number} The rounded color. - */ - roundColor: (color) => - { - const step = CanvasTinter.cacheStepsPerColorChannel; - - const rgbValues = hex2rgb(color); - - rgbValues[0] = Math.min(255, (rgbValues[0] / step) * step); - rgbValues[1] = Math.min(255, (rgbValues[1] / step) * step); - rgbValues[2] = Math.min(255, (rgbValues[2] / step) * step); - - return rgb2hex(rgbValues); - }, - - /** - * Number of steps which will be used as a cap when rounding colors. - * - * @memberof PIXI.CanvasTinter - * @type {number} - */ - cacheStepsPerColorChannel: 8, - - /** - * Tint cache boolean flag. - * - * @memberof PIXI.CanvasTinter - * @type {boolean} - */ - convertTintToImage: false, - - /** - * Whether or not the Canvas BlendModes are supported, consequently the ability to tint using the multiply method. - * - * @memberof PIXI.CanvasTinter - * @type {boolean} - */ - canUseMultiply: canUseNewCanvasBlendModes(), - - /** - * The tinting method that will be used. - * - * @memberof PIXI.CanvasTinter - * @type {Function} - */ - tintMethod: () => - { // jslint-disable no-empty-function - - }, -}; - -CanvasTinter.tintMethod = CanvasTinter.canUseMultiply ? CanvasTinter.tintWithMultiply : CanvasTinter.tintWithPerPixel; diff --git a/packages/canvas/canvas-renderer/src/canvasUtils.js b/packages/canvas/canvas-renderer/src/canvasUtils.js new file mode 100644 index 0000000..62e16d4 --- /dev/null +++ b/packages/canvas/canvas-renderer/src/canvasUtils.js @@ -0,0 +1,288 @@ +import { hex2rgb, rgb2hex } from '@pixi/utils'; +import { canUseNewCanvasBlendModes } from './utils/canUseNewCanvasBlendModes'; + +/** + * Utility methods for Sprite/Texture tinting. + * + * Tinting with the CanvasRenderer involves creating a new canvas to use as a texture, + * so be aware of the performance implications. + * + * @namespace PIXI.canvasUtils + * @memberof PIXI + */ +export const canvasUtils = { + /** + * Basically this method just needs a sprite and a color and tints the sprite with the given color. + * + * @memberof PIXI.canvasUtils + * @param {PIXI.Sprite} sprite - the sprite to tint + * @param {number} color - the color to use to tint the sprite with + * @return {HTMLCanvasElement} The tinted canvas + */ + getTintedCanvas: (sprite, color) => + { + const texture = sprite.texture; + + color = canvasUtils.roundColor(color); + + const stringColor = `#${(`00000${(color | 0).toString(16)}`).substr(-6)}`; + + texture.tintCache = texture.tintCache || {}; + + const cachedCanvas = texture.tintCache[stringColor]; + + let canvas; + + if (cachedCanvas) + { + if (cachedCanvas.tintId === texture._updateID) + { + return texture.tintCache[stringColor]; + } + + canvas = texture.tintCache[stringColor]; + } + else + { + canvas = canvasUtils.canvas || document.createElement('canvas'); + } + + canvasUtils.tintMethod(texture, color, canvas); + + canvas.tintId = texture._updateID; + + if (canvasUtils.convertTintToImage) + { + // is this better? + const tintImage = new Image(); + + tintImage.src = canvas.toDataURL(); + + texture.tintCache[stringColor] = tintImage; + } + else + { + texture.tintCache[stringColor] = canvas; + // if we are not converting the texture to an image then we need to lose the reference to the canvas + canvasUtils.canvas = null; + } + + return canvas; + }, + + /** + * Tint a texture using the 'multiply' operation. + * + * @memberof PIXI.canvasUtils + * @param {PIXI.Texture} texture - the texture to tint + * @param {number} color - the color to use to tint the sprite with + * @param {HTMLCanvasElement} canvas - the current canvas + */ + tintWithMultiply: (texture, color, canvas) => + { + const context = canvas.getContext('2d'); + const crop = texture._frame.clone(); + const resolution = texture.baseTexture.resolution; + + crop.x *= resolution; + crop.y *= resolution; + crop.width *= resolution; + crop.height *= resolution; + + canvas.width = Math.ceil(crop.width); + canvas.height = Math.ceil(crop.height); + + context.save(); + context.fillStyle = `#${(`00000${(color | 0).toString(16)}`).substr(-6)}`; + + context.fillRect(0, 0, crop.width, crop.height); + + context.globalCompositeOperation = 'multiply'; + + const source = texture.baseTexture.getDrawableSource(); + + context.drawImage( + source, + crop.x, + crop.y, + crop.width, + crop.height, + 0, + 0, + crop.width, + crop.height + ); + + context.globalCompositeOperation = 'destination-atop'; + + context.drawImage( + source, + crop.x, + crop.y, + crop.width, + crop.height, + 0, + 0, + crop.width, + crop.height + ); + context.restore(); + }, + + /** + * Tint a texture using the 'overlay' operation. + * + * @memberof PIXI.canvasUtils + * @param {PIXI.Texture} texture - the texture to tint + * @param {number} color - the color to use to tint the sprite with + * @param {HTMLCanvasElement} canvas - the current canvas + */ + tintWithOverlay(texture, color, canvas) + { + const context = canvas.getContext('2d'); + const crop = texture._frame.clone(); + const resolution = texture.baseTexture.resolution; + + crop.x *= resolution; + crop.y *= resolution; + crop.width *= resolution; + crop.height *= resolution; + + canvas.width = Math.ceil(crop.width); + canvas.height = Math.ceil(crop.height); + + context.save(); + context.globalCompositeOperation = 'copy'; + context.fillStyle = `#${(`00000${(color | 0).toString(16)}`).substr(-6)}`; + context.fillRect(0, 0, crop.width, crop.height); + + context.globalCompositeOperation = 'destination-atop'; + context.drawImage( + texture.baseTexture.getDrawableSource(), + crop.x, + crop.y, + crop.width, + crop.height, + 0, + 0, + crop.width, + crop.height + ); + + // context.globalCompositeOperation = 'copy'; + context.restore(); + }, + + /** + * Tint a texture pixel per pixel. + * + * @memberof PIXI.canvasUtils + * @param {PIXI.Texture} texture - the texture to tint + * @param {number} color - the color to use to tint the sprite with + * @param {HTMLCanvasElement} canvas - the current canvas + */ + tintWithPerPixel: (texture, color, canvas) => + { + const context = canvas.getContext('2d'); + const crop = texture._frame.clone(); + const resolution = texture.baseTexture.resolution; + + crop.x *= resolution; + crop.y *= resolution; + crop.width *= resolution; + crop.height *= resolution; + + canvas.width = Math.ceil(crop.width); + canvas.height = Math.ceil(crop.height); + + context.save(); + context.globalCompositeOperation = 'copy'; + context.drawImage( + texture.baseTexture.getDrawableSource(), + crop.x, + crop.y, + crop.width, + crop.height, + 0, + 0, + crop.width, + crop.height + ); + context.restore(); + + const rgbValues = hex2rgb(color); + const r = rgbValues[0]; + const g = rgbValues[1]; + const b = rgbValues[2]; + + const pixelData = context.getImageData(0, 0, crop.width, crop.height); + + const pixels = pixelData.data; + + for (let i = 0; i < pixels.length; i += 4) + { + pixels[i + 0] *= r; + pixels[i + 1] *= g; + pixels[i + 2] *= b; + } + + context.putImageData(pixelData, 0, 0); + }, + + /** + * Rounds the specified color according to the canvasUtils.cacheStepsPerColorChannel. + * + * @memberof PIXI.canvasUtils + * @param {number} color - the color to round, should be a hex color + * @return {number} The rounded color. + */ + roundColor: (color) => + { + const step = canvasUtils.cacheStepsPerColorChannel; + + const rgbValues = hex2rgb(color); + + rgbValues[0] = Math.min(255, (rgbValues[0] / step) * step); + rgbValues[1] = Math.min(255, (rgbValues[1] / step) * step); + rgbValues[2] = Math.min(255, (rgbValues[2] / step) * step); + + return rgb2hex(rgbValues); + }, + + /** + * Number of steps which will be used as a cap when rounding colors. + * + * @memberof PIXI.canvasUtils + * @type {number} + */ + cacheStepsPerColorChannel: 8, + + /** + * Tint cache boolean flag. + * + * @memberof PIXI.canvasUtils + * @type {boolean} + */ + convertTintToImage: false, + + /** + * Whether or not the Canvas BlendModes are supported, consequently the ability to tint using the multiply method. + * + * @memberof PIXI.canvasUtils + * @type {boolean} + */ + canUseMultiply: canUseNewCanvasBlendModes(), + + /** + * The tinting method that will be used. + * + * @memberof PIXI.canvasUtils + * @type {Function} + */ + tintMethod: () => + { // jslint-disable no-empty-function + + }, +}; + +canvasUtils.tintMethod = canvasUtils.canUseMultiply ? canvasUtils.tintWithMultiply : canvasUtils.tintWithPerPixel; diff --git a/packages/canvas/canvas-renderer/src/index.js b/packages/canvas/canvas-renderer/src/index.js index 80987c3..71bc5ee 100644 --- a/packages/canvas/canvas-renderer/src/index.js +++ b/packages/canvas/canvas-renderer/src/index.js @@ -1,6 +1,6 @@ export * from './CanvasRenderer'; export * from './utils/canUseNewCanvasBlendModes'; -export * from './CanvasTinter'; +export * from './canvasUtils'; import './Renderer'; import './BaseTexture'; diff --git a/bundles/pixi.js-legacy/src/index.js b/bundles/pixi.js-legacy/src/index.js index f1598a1..df6b157 100644 --- a/bundles/pixi.js-legacy/src/index.js +++ b/bundles/pixi.js-legacy/src/index.js @@ -1,5 +1,5 @@ import { accessibility, interaction, prepare, extract } from 'pixi.js'; -import { CanvasRenderer, CanvasTinter } from '@pixi/canvas-renderer'; +import { CanvasRenderer, canvasUtils } from '@pixi/canvas-renderer'; import { CanvasMeshRenderer } from '@pixi/canvas-mesh'; import { CanvasGraphicsRenderer } from '@pixi/canvas-graphics'; import { CanvasSpriteRenderer } from '@pixi/canvas-sprite'; @@ -29,5 +29,5 @@ CanvasGraphicsRenderer, CanvasMeshRenderer, CanvasSpriteRenderer, - CanvasTinter, + canvasUtils, }; diff --git a/bundles/pixi.js/src/useDeprecated.js b/bundles/pixi.js/src/useDeprecated.js index 12f00d4..ab88092 100644 --- a/bundles/pixi.js/src/useDeprecated.js +++ b/bundles/pixi.js/src/useDeprecated.js @@ -138,6 +138,34 @@ return PIXI.systems.FilterSystem; }, }, + + /** + * @namespace PIXI.CanvasTinter + * @see PIXI.canvasUtils + * @deprecated since 5.2.0 + */ + CanvasTinter: { + get() + { + deprecation('5.2.0', 'PIXI.CanvasTinter namespace has moved to PIXI.canvasUtils'); + + return PIXI.canvasUtils; + }, + }, + + /** + * @namespace PIXI.GroupD8 + * @see PIXI.groupD8 + * @deprecated since 5.2.0 + */ + GroupD8: { + get() + { + deprecation('5.2.0', 'PIXI.GroupD8 namespace has moved to PIXI.groupD8'); + + return PIXI.groupD8; + }, + }, }); /** diff --git a/packages/canvas/canvas-mesh/src/NineSlicePlane.js b/packages/canvas/canvas-mesh/src/NineSlicePlane.js index dccfd79..6e2f055 100644 --- a/packages/canvas/canvas-mesh/src/NineSlicePlane.js +++ b/packages/canvas/canvas-mesh/src/NineSlicePlane.js @@ -1,4 +1,4 @@ -import { CanvasTinter } from '@pixi/canvas-renderer'; +import { canvasUtils } from '@pixi/canvas-renderer'; import { NineSlicePlane } from '@pixi/mesh-extras'; /** @@ -50,7 +50,7 @@ this._cachedTint = this.tint; - this._tintedCanvas = CanvasTinter.getTintedCanvas(this, this.tint); + this._tintedCanvas = canvasUtils.getTintedCanvas(this, this.tint); } } diff --git a/packages/canvas/canvas-renderer/src/CanvasTinter.js b/packages/canvas/canvas-renderer/src/CanvasTinter.js deleted file mode 100644 index bfec51a..0000000 --- a/packages/canvas/canvas-renderer/src/CanvasTinter.js +++ /dev/null @@ -1,288 +0,0 @@ -import { hex2rgb, rgb2hex } from '@pixi/utils'; -import { canUseNewCanvasBlendModes } from './utils/canUseNewCanvasBlendModes'; - -/** - * Utility methods for Sprite/Texture tinting. - * - * Tinting with the CanvasRenderer involves creating a new canvas to use as a texture, - * so be aware of the performance implications. - * - * @namespace PIXI.CanvasTinter - * @memberof PIXI - */ -export const CanvasTinter = { - /** - * Basically this method just needs a sprite and a color and tints the sprite with the given color. - * - * @memberof PIXI.CanvasTinter - * @param {PIXI.Sprite} sprite - the sprite to tint - * @param {number} color - the color to use to tint the sprite with - * @return {HTMLCanvasElement} The tinted canvas - */ - getTintedCanvas: (sprite, color) => - { - const texture = sprite.texture; - - color = CanvasTinter.roundColor(color); - - const stringColor = `#${(`00000${(color | 0).toString(16)}`).substr(-6)}`; - - texture.tintCache = texture.tintCache || {}; - - const cachedCanvas = texture.tintCache[stringColor]; - - let canvas; - - if (cachedCanvas) - { - if (cachedCanvas.tintId === texture._updateID) - { - return texture.tintCache[stringColor]; - } - - canvas = texture.tintCache[stringColor]; - } - else - { - canvas = CanvasTinter.canvas || document.createElement('canvas'); - } - - CanvasTinter.tintMethod(texture, color, canvas); - - canvas.tintId = texture._updateID; - - if (CanvasTinter.convertTintToImage) - { - // is this better? - const tintImage = new Image(); - - tintImage.src = canvas.toDataURL(); - - texture.tintCache[stringColor] = tintImage; - } - else - { - texture.tintCache[stringColor] = canvas; - // if we are not converting the texture to an image then we need to lose the reference to the canvas - CanvasTinter.canvas = null; - } - - return canvas; - }, - - /** - * Tint a texture using the 'multiply' operation. - * - * @memberof PIXI.CanvasTinter - * @param {PIXI.Texture} texture - the texture to tint - * @param {number} color - the color to use to tint the sprite with - * @param {HTMLCanvasElement} canvas - the current canvas - */ - tintWithMultiply: (texture, color, canvas) => - { - const context = canvas.getContext('2d'); - const crop = texture._frame.clone(); - const resolution = texture.baseTexture.resolution; - - crop.x *= resolution; - crop.y *= resolution; - crop.width *= resolution; - crop.height *= resolution; - - canvas.width = Math.ceil(crop.width); - canvas.height = Math.ceil(crop.height); - - context.save(); - context.fillStyle = `#${(`00000${(color | 0).toString(16)}`).substr(-6)}`; - - context.fillRect(0, 0, crop.width, crop.height); - - context.globalCompositeOperation = 'multiply'; - - const source = texture.baseTexture.getDrawableSource(); - - context.drawImage( - source, - crop.x, - crop.y, - crop.width, - crop.height, - 0, - 0, - crop.width, - crop.height - ); - - context.globalCompositeOperation = 'destination-atop'; - - context.drawImage( - source, - crop.x, - crop.y, - crop.width, - crop.height, - 0, - 0, - crop.width, - crop.height - ); - context.restore(); - }, - - /** - * Tint a texture using the 'overlay' operation. - * - * @memberof PIXI.CanvasTinter - * @param {PIXI.Texture} texture - the texture to tint - * @param {number} color - the color to use to tint the sprite with - * @param {HTMLCanvasElement} canvas - the current canvas - */ - tintWithOverlay(texture, color, canvas) - { - const context = canvas.getContext('2d'); - const crop = texture._frame.clone(); - const resolution = texture.baseTexture.resolution; - - crop.x *= resolution; - crop.y *= resolution; - crop.width *= resolution; - crop.height *= resolution; - - canvas.width = Math.ceil(crop.width); - canvas.height = Math.ceil(crop.height); - - context.save(); - context.globalCompositeOperation = 'copy'; - context.fillStyle = `#${(`00000${(color | 0).toString(16)}`).substr(-6)}`; - context.fillRect(0, 0, crop.width, crop.height); - - context.globalCompositeOperation = 'destination-atop'; - context.drawImage( - texture.baseTexture.getDrawableSource(), - crop.x, - crop.y, - crop.width, - crop.height, - 0, - 0, - crop.width, - crop.height - ); - - // context.globalCompositeOperation = 'copy'; - context.restore(); - }, - - /** - * Tint a texture pixel per pixel. - * - * @memberof PIXI.CanvasTinter - * @param {PIXI.Texture} texture - the texture to tint - * @param {number} color - the color to use to tint the sprite with - * @param {HTMLCanvasElement} canvas - the current canvas - */ - tintWithPerPixel: (texture, color, canvas) => - { - const context = canvas.getContext('2d'); - const crop = texture._frame.clone(); - const resolution = texture.baseTexture.resolution; - - crop.x *= resolution; - crop.y *= resolution; - crop.width *= resolution; - crop.height *= resolution; - - canvas.width = Math.ceil(crop.width); - canvas.height = Math.ceil(crop.height); - - context.save(); - context.globalCompositeOperation = 'copy'; - context.drawImage( - texture.baseTexture.getDrawableSource(), - crop.x, - crop.y, - crop.width, - crop.height, - 0, - 0, - crop.width, - crop.height - ); - context.restore(); - - const rgbValues = hex2rgb(color); - const r = rgbValues[0]; - const g = rgbValues[1]; - const b = rgbValues[2]; - - const pixelData = context.getImageData(0, 0, crop.width, crop.height); - - const pixels = pixelData.data; - - for (let i = 0; i < pixels.length; i += 4) - { - pixels[i + 0] *= r; - pixels[i + 1] *= g; - pixels[i + 2] *= b; - } - - context.putImageData(pixelData, 0, 0); - }, - - /** - * Rounds the specified color according to the CanvasTinter.cacheStepsPerColorChannel. - * - * @memberof PIXI.CanvasTinter - * @param {number} color - the color to round, should be a hex color - * @return {number} The rounded color. - */ - roundColor: (color) => - { - const step = CanvasTinter.cacheStepsPerColorChannel; - - const rgbValues = hex2rgb(color); - - rgbValues[0] = Math.min(255, (rgbValues[0] / step) * step); - rgbValues[1] = Math.min(255, (rgbValues[1] / step) * step); - rgbValues[2] = Math.min(255, (rgbValues[2] / step) * step); - - return rgb2hex(rgbValues); - }, - - /** - * Number of steps which will be used as a cap when rounding colors. - * - * @memberof PIXI.CanvasTinter - * @type {number} - */ - cacheStepsPerColorChannel: 8, - - /** - * Tint cache boolean flag. - * - * @memberof PIXI.CanvasTinter - * @type {boolean} - */ - convertTintToImage: false, - - /** - * Whether or not the Canvas BlendModes are supported, consequently the ability to tint using the multiply method. - * - * @memberof PIXI.CanvasTinter - * @type {boolean} - */ - canUseMultiply: canUseNewCanvasBlendModes(), - - /** - * The tinting method that will be used. - * - * @memberof PIXI.CanvasTinter - * @type {Function} - */ - tintMethod: () => - { // jslint-disable no-empty-function - - }, -}; - -CanvasTinter.tintMethod = CanvasTinter.canUseMultiply ? CanvasTinter.tintWithMultiply : CanvasTinter.tintWithPerPixel; diff --git a/packages/canvas/canvas-renderer/src/canvasUtils.js b/packages/canvas/canvas-renderer/src/canvasUtils.js new file mode 100644 index 0000000..62e16d4 --- /dev/null +++ b/packages/canvas/canvas-renderer/src/canvasUtils.js @@ -0,0 +1,288 @@ +import { hex2rgb, rgb2hex } from '@pixi/utils'; +import { canUseNewCanvasBlendModes } from './utils/canUseNewCanvasBlendModes'; + +/** + * Utility methods for Sprite/Texture tinting. + * + * Tinting with the CanvasRenderer involves creating a new canvas to use as a texture, + * so be aware of the performance implications. + * + * @namespace PIXI.canvasUtils + * @memberof PIXI + */ +export const canvasUtils = { + /** + * Basically this method just needs a sprite and a color and tints the sprite with the given color. + * + * @memberof PIXI.canvasUtils + * @param {PIXI.Sprite} sprite - the sprite to tint + * @param {number} color - the color to use to tint the sprite with + * @return {HTMLCanvasElement} The tinted canvas + */ + getTintedCanvas: (sprite, color) => + { + const texture = sprite.texture; + + color = canvasUtils.roundColor(color); + + const stringColor = `#${(`00000${(color | 0).toString(16)}`).substr(-6)}`; + + texture.tintCache = texture.tintCache || {}; + + const cachedCanvas = texture.tintCache[stringColor]; + + let canvas; + + if (cachedCanvas) + { + if (cachedCanvas.tintId === texture._updateID) + { + return texture.tintCache[stringColor]; + } + + canvas = texture.tintCache[stringColor]; + } + else + { + canvas = canvasUtils.canvas || document.createElement('canvas'); + } + + canvasUtils.tintMethod(texture, color, canvas); + + canvas.tintId = texture._updateID; + + if (canvasUtils.convertTintToImage) + { + // is this better? + const tintImage = new Image(); + + tintImage.src = canvas.toDataURL(); + + texture.tintCache[stringColor] = tintImage; + } + else + { + texture.tintCache[stringColor] = canvas; + // if we are not converting the texture to an image then we need to lose the reference to the canvas + canvasUtils.canvas = null; + } + + return canvas; + }, + + /** + * Tint a texture using the 'multiply' operation. + * + * @memberof PIXI.canvasUtils + * @param {PIXI.Texture} texture - the texture to tint + * @param {number} color - the color to use to tint the sprite with + * @param {HTMLCanvasElement} canvas - the current canvas + */ + tintWithMultiply: (texture, color, canvas) => + { + const context = canvas.getContext('2d'); + const crop = texture._frame.clone(); + const resolution = texture.baseTexture.resolution; + + crop.x *= resolution; + crop.y *= resolution; + crop.width *= resolution; + crop.height *= resolution; + + canvas.width = Math.ceil(crop.width); + canvas.height = Math.ceil(crop.height); + + context.save(); + context.fillStyle = `#${(`00000${(color | 0).toString(16)}`).substr(-6)}`; + + context.fillRect(0, 0, crop.width, crop.height); + + context.globalCompositeOperation = 'multiply'; + + const source = texture.baseTexture.getDrawableSource(); + + context.drawImage( + source, + crop.x, + crop.y, + crop.width, + crop.height, + 0, + 0, + crop.width, + crop.height + ); + + context.globalCompositeOperation = 'destination-atop'; + + context.drawImage( + source, + crop.x, + crop.y, + crop.width, + crop.height, + 0, + 0, + crop.width, + crop.height + ); + context.restore(); + }, + + /** + * Tint a texture using the 'overlay' operation. + * + * @memberof PIXI.canvasUtils + * @param {PIXI.Texture} texture - the texture to tint + * @param {number} color - the color to use to tint the sprite with + * @param {HTMLCanvasElement} canvas - the current canvas + */ + tintWithOverlay(texture, color, canvas) + { + const context = canvas.getContext('2d'); + const crop = texture._frame.clone(); + const resolution = texture.baseTexture.resolution; + + crop.x *= resolution; + crop.y *= resolution; + crop.width *= resolution; + crop.height *= resolution; + + canvas.width = Math.ceil(crop.width); + canvas.height = Math.ceil(crop.height); + + context.save(); + context.globalCompositeOperation = 'copy'; + context.fillStyle = `#${(`00000${(color | 0).toString(16)}`).substr(-6)}`; + context.fillRect(0, 0, crop.width, crop.height); + + context.globalCompositeOperation = 'destination-atop'; + context.drawImage( + texture.baseTexture.getDrawableSource(), + crop.x, + crop.y, + crop.width, + crop.height, + 0, + 0, + crop.width, + crop.height + ); + + // context.globalCompositeOperation = 'copy'; + context.restore(); + }, + + /** + * Tint a texture pixel per pixel. + * + * @memberof PIXI.canvasUtils + * @param {PIXI.Texture} texture - the texture to tint + * @param {number} color - the color to use to tint the sprite with + * @param {HTMLCanvasElement} canvas - the current canvas + */ + tintWithPerPixel: (texture, color, canvas) => + { + const context = canvas.getContext('2d'); + const crop = texture._frame.clone(); + const resolution = texture.baseTexture.resolution; + + crop.x *= resolution; + crop.y *= resolution; + crop.width *= resolution; + crop.height *= resolution; + + canvas.width = Math.ceil(crop.width); + canvas.height = Math.ceil(crop.height); + + context.save(); + context.globalCompositeOperation = 'copy'; + context.drawImage( + texture.baseTexture.getDrawableSource(), + crop.x, + crop.y, + crop.width, + crop.height, + 0, + 0, + crop.width, + crop.height + ); + context.restore(); + + const rgbValues = hex2rgb(color); + const r = rgbValues[0]; + const g = rgbValues[1]; + const b = rgbValues[2]; + + const pixelData = context.getImageData(0, 0, crop.width, crop.height); + + const pixels = pixelData.data; + + for (let i = 0; i < pixels.length; i += 4) + { + pixels[i + 0] *= r; + pixels[i + 1] *= g; + pixels[i + 2] *= b; + } + + context.putImageData(pixelData, 0, 0); + }, + + /** + * Rounds the specified color according to the canvasUtils.cacheStepsPerColorChannel. + * + * @memberof PIXI.canvasUtils + * @param {number} color - the color to round, should be a hex color + * @return {number} The rounded color. + */ + roundColor: (color) => + { + const step = canvasUtils.cacheStepsPerColorChannel; + + const rgbValues = hex2rgb(color); + + rgbValues[0] = Math.min(255, (rgbValues[0] / step) * step); + rgbValues[1] = Math.min(255, (rgbValues[1] / step) * step); + rgbValues[2] = Math.min(255, (rgbValues[2] / step) * step); + + return rgb2hex(rgbValues); + }, + + /** + * Number of steps which will be used as a cap when rounding colors. + * + * @memberof PIXI.canvasUtils + * @type {number} + */ + cacheStepsPerColorChannel: 8, + + /** + * Tint cache boolean flag. + * + * @memberof PIXI.canvasUtils + * @type {boolean} + */ + convertTintToImage: false, + + /** + * Whether or not the Canvas BlendModes are supported, consequently the ability to tint using the multiply method. + * + * @memberof PIXI.canvasUtils + * @type {boolean} + */ + canUseMultiply: canUseNewCanvasBlendModes(), + + /** + * The tinting method that will be used. + * + * @memberof PIXI.canvasUtils + * @type {Function} + */ + tintMethod: () => + { // jslint-disable no-empty-function + + }, +}; + +canvasUtils.tintMethod = canvasUtils.canUseMultiply ? canvasUtils.tintWithMultiply : canvasUtils.tintWithPerPixel; diff --git a/packages/canvas/canvas-renderer/src/index.js b/packages/canvas/canvas-renderer/src/index.js index 80987c3..71bc5ee 100644 --- a/packages/canvas/canvas-renderer/src/index.js +++ b/packages/canvas/canvas-renderer/src/index.js @@ -1,6 +1,6 @@ export * from './CanvasRenderer'; export * from './utils/canUseNewCanvasBlendModes'; -export * from './CanvasTinter'; +export * from './canvasUtils'; import './Renderer'; import './BaseTexture'; diff --git a/packages/canvas/canvas-sprite-tiling/src/TilingSprite.js b/packages/canvas/canvas-sprite-tiling/src/TilingSprite.js index 4eac1d7..fd8857f 100644 --- a/packages/canvas/canvas-sprite-tiling/src/TilingSprite.js +++ b/packages/canvas/canvas-sprite-tiling/src/TilingSprite.js @@ -1,5 +1,5 @@ import { TilingSprite } from '@pixi/sprite-tiling'; -import { CanvasTinter } from '@pixi/canvas-renderer'; +import { canvasUtils } from '@pixi/canvas-renderer'; import { CanvasRenderTarget } from '@pixi/utils'; /** @@ -40,7 +40,7 @@ // Tint the tiling sprite if (this.tint !== 0xFFFFFF) { - this._tintedCanvas = CanvasTinter.getTintedCanvas(this, this.tint); + this._tintedCanvas = canvasUtils.getTintedCanvas(this, this.tint); tempCanvas.context.drawImage(this._tintedCanvas, 0, 0); } else diff --git a/bundles/pixi.js-legacy/src/index.js b/bundles/pixi.js-legacy/src/index.js index f1598a1..df6b157 100644 --- a/bundles/pixi.js-legacy/src/index.js +++ b/bundles/pixi.js-legacy/src/index.js @@ -1,5 +1,5 @@ import { accessibility, interaction, prepare, extract } from 'pixi.js'; -import { CanvasRenderer, CanvasTinter } from '@pixi/canvas-renderer'; +import { CanvasRenderer, canvasUtils } from '@pixi/canvas-renderer'; import { CanvasMeshRenderer } from '@pixi/canvas-mesh'; import { CanvasGraphicsRenderer } from '@pixi/canvas-graphics'; import { CanvasSpriteRenderer } from '@pixi/canvas-sprite'; @@ -29,5 +29,5 @@ CanvasGraphicsRenderer, CanvasMeshRenderer, CanvasSpriteRenderer, - CanvasTinter, + canvasUtils, }; diff --git a/bundles/pixi.js/src/useDeprecated.js b/bundles/pixi.js/src/useDeprecated.js index 12f00d4..ab88092 100644 --- a/bundles/pixi.js/src/useDeprecated.js +++ b/bundles/pixi.js/src/useDeprecated.js @@ -138,6 +138,34 @@ return PIXI.systems.FilterSystem; }, }, + + /** + * @namespace PIXI.CanvasTinter + * @see PIXI.canvasUtils + * @deprecated since 5.2.0 + */ + CanvasTinter: { + get() + { + deprecation('5.2.0', 'PIXI.CanvasTinter namespace has moved to PIXI.canvasUtils'); + + return PIXI.canvasUtils; + }, + }, + + /** + * @namespace PIXI.GroupD8 + * @see PIXI.groupD8 + * @deprecated since 5.2.0 + */ + GroupD8: { + get() + { + deprecation('5.2.0', 'PIXI.GroupD8 namespace has moved to PIXI.groupD8'); + + return PIXI.groupD8; + }, + }, }); /** diff --git a/packages/canvas/canvas-mesh/src/NineSlicePlane.js b/packages/canvas/canvas-mesh/src/NineSlicePlane.js index dccfd79..6e2f055 100644 --- a/packages/canvas/canvas-mesh/src/NineSlicePlane.js +++ b/packages/canvas/canvas-mesh/src/NineSlicePlane.js @@ -1,4 +1,4 @@ -import { CanvasTinter } from '@pixi/canvas-renderer'; +import { canvasUtils } from '@pixi/canvas-renderer'; import { NineSlicePlane } from '@pixi/mesh-extras'; /** @@ -50,7 +50,7 @@ this._cachedTint = this.tint; - this._tintedCanvas = CanvasTinter.getTintedCanvas(this, this.tint); + this._tintedCanvas = canvasUtils.getTintedCanvas(this, this.tint); } } diff --git a/packages/canvas/canvas-renderer/src/CanvasTinter.js b/packages/canvas/canvas-renderer/src/CanvasTinter.js deleted file mode 100644 index bfec51a..0000000 --- a/packages/canvas/canvas-renderer/src/CanvasTinter.js +++ /dev/null @@ -1,288 +0,0 @@ -import { hex2rgb, rgb2hex } from '@pixi/utils'; -import { canUseNewCanvasBlendModes } from './utils/canUseNewCanvasBlendModes'; - -/** - * Utility methods for Sprite/Texture tinting. - * - * Tinting with the CanvasRenderer involves creating a new canvas to use as a texture, - * so be aware of the performance implications. - * - * @namespace PIXI.CanvasTinter - * @memberof PIXI - */ -export const CanvasTinter = { - /** - * Basically this method just needs a sprite and a color and tints the sprite with the given color. - * - * @memberof PIXI.CanvasTinter - * @param {PIXI.Sprite} sprite - the sprite to tint - * @param {number} color - the color to use to tint the sprite with - * @return {HTMLCanvasElement} The tinted canvas - */ - getTintedCanvas: (sprite, color) => - { - const texture = sprite.texture; - - color = CanvasTinter.roundColor(color); - - const stringColor = `#${(`00000${(color | 0).toString(16)}`).substr(-6)}`; - - texture.tintCache = texture.tintCache || {}; - - const cachedCanvas = texture.tintCache[stringColor]; - - let canvas; - - if (cachedCanvas) - { - if (cachedCanvas.tintId === texture._updateID) - { - return texture.tintCache[stringColor]; - } - - canvas = texture.tintCache[stringColor]; - } - else - { - canvas = CanvasTinter.canvas || document.createElement('canvas'); - } - - CanvasTinter.tintMethod(texture, color, canvas); - - canvas.tintId = texture._updateID; - - if (CanvasTinter.convertTintToImage) - { - // is this better? - const tintImage = new Image(); - - tintImage.src = canvas.toDataURL(); - - texture.tintCache[stringColor] = tintImage; - } - else - { - texture.tintCache[stringColor] = canvas; - // if we are not converting the texture to an image then we need to lose the reference to the canvas - CanvasTinter.canvas = null; - } - - return canvas; - }, - - /** - * Tint a texture using the 'multiply' operation. - * - * @memberof PIXI.CanvasTinter - * @param {PIXI.Texture} texture - the texture to tint - * @param {number} color - the color to use to tint the sprite with - * @param {HTMLCanvasElement} canvas - the current canvas - */ - tintWithMultiply: (texture, color, canvas) => - { - const context = canvas.getContext('2d'); - const crop = texture._frame.clone(); - const resolution = texture.baseTexture.resolution; - - crop.x *= resolution; - crop.y *= resolution; - crop.width *= resolution; - crop.height *= resolution; - - canvas.width = Math.ceil(crop.width); - canvas.height = Math.ceil(crop.height); - - context.save(); - context.fillStyle = `#${(`00000${(color | 0).toString(16)}`).substr(-6)}`; - - context.fillRect(0, 0, crop.width, crop.height); - - context.globalCompositeOperation = 'multiply'; - - const source = texture.baseTexture.getDrawableSource(); - - context.drawImage( - source, - crop.x, - crop.y, - crop.width, - crop.height, - 0, - 0, - crop.width, - crop.height - ); - - context.globalCompositeOperation = 'destination-atop'; - - context.drawImage( - source, - crop.x, - crop.y, - crop.width, - crop.height, - 0, - 0, - crop.width, - crop.height - ); - context.restore(); - }, - - /** - * Tint a texture using the 'overlay' operation. - * - * @memberof PIXI.CanvasTinter - * @param {PIXI.Texture} texture - the texture to tint - * @param {number} color - the color to use to tint the sprite with - * @param {HTMLCanvasElement} canvas - the current canvas - */ - tintWithOverlay(texture, color, canvas) - { - const context = canvas.getContext('2d'); - const crop = texture._frame.clone(); - const resolution = texture.baseTexture.resolution; - - crop.x *= resolution; - crop.y *= resolution; - crop.width *= resolution; - crop.height *= resolution; - - canvas.width = Math.ceil(crop.width); - canvas.height = Math.ceil(crop.height); - - context.save(); - context.globalCompositeOperation = 'copy'; - context.fillStyle = `#${(`00000${(color | 0).toString(16)}`).substr(-6)}`; - context.fillRect(0, 0, crop.width, crop.height); - - context.globalCompositeOperation = 'destination-atop'; - context.drawImage( - texture.baseTexture.getDrawableSource(), - crop.x, - crop.y, - crop.width, - crop.height, - 0, - 0, - crop.width, - crop.height - ); - - // context.globalCompositeOperation = 'copy'; - context.restore(); - }, - - /** - * Tint a texture pixel per pixel. - * - * @memberof PIXI.CanvasTinter - * @param {PIXI.Texture} texture - the texture to tint - * @param {number} color - the color to use to tint the sprite with - * @param {HTMLCanvasElement} canvas - the current canvas - */ - tintWithPerPixel: (texture, color, canvas) => - { - const context = canvas.getContext('2d'); - const crop = texture._frame.clone(); - const resolution = texture.baseTexture.resolution; - - crop.x *= resolution; - crop.y *= resolution; - crop.width *= resolution; - crop.height *= resolution; - - canvas.width = Math.ceil(crop.width); - canvas.height = Math.ceil(crop.height); - - context.save(); - context.globalCompositeOperation = 'copy'; - context.drawImage( - texture.baseTexture.getDrawableSource(), - crop.x, - crop.y, - crop.width, - crop.height, - 0, - 0, - crop.width, - crop.height - ); - context.restore(); - - const rgbValues = hex2rgb(color); - const r = rgbValues[0]; - const g = rgbValues[1]; - const b = rgbValues[2]; - - const pixelData = context.getImageData(0, 0, crop.width, crop.height); - - const pixels = pixelData.data; - - for (let i = 0; i < pixels.length; i += 4) - { - pixels[i + 0] *= r; - pixels[i + 1] *= g; - pixels[i + 2] *= b; - } - - context.putImageData(pixelData, 0, 0); - }, - - /** - * Rounds the specified color according to the CanvasTinter.cacheStepsPerColorChannel. - * - * @memberof PIXI.CanvasTinter - * @param {number} color - the color to round, should be a hex color - * @return {number} The rounded color. - */ - roundColor: (color) => - { - const step = CanvasTinter.cacheStepsPerColorChannel; - - const rgbValues = hex2rgb(color); - - rgbValues[0] = Math.min(255, (rgbValues[0] / step) * step); - rgbValues[1] = Math.min(255, (rgbValues[1] / step) * step); - rgbValues[2] = Math.min(255, (rgbValues[2] / step) * step); - - return rgb2hex(rgbValues); - }, - - /** - * Number of steps which will be used as a cap when rounding colors. - * - * @memberof PIXI.CanvasTinter - * @type {number} - */ - cacheStepsPerColorChannel: 8, - - /** - * Tint cache boolean flag. - * - * @memberof PIXI.CanvasTinter - * @type {boolean} - */ - convertTintToImage: false, - - /** - * Whether or not the Canvas BlendModes are supported, consequently the ability to tint using the multiply method. - * - * @memberof PIXI.CanvasTinter - * @type {boolean} - */ - canUseMultiply: canUseNewCanvasBlendModes(), - - /** - * The tinting method that will be used. - * - * @memberof PIXI.CanvasTinter - * @type {Function} - */ - tintMethod: () => - { // jslint-disable no-empty-function - - }, -}; - -CanvasTinter.tintMethod = CanvasTinter.canUseMultiply ? CanvasTinter.tintWithMultiply : CanvasTinter.tintWithPerPixel; diff --git a/packages/canvas/canvas-renderer/src/canvasUtils.js b/packages/canvas/canvas-renderer/src/canvasUtils.js new file mode 100644 index 0000000..62e16d4 --- /dev/null +++ b/packages/canvas/canvas-renderer/src/canvasUtils.js @@ -0,0 +1,288 @@ +import { hex2rgb, rgb2hex } from '@pixi/utils'; +import { canUseNewCanvasBlendModes } from './utils/canUseNewCanvasBlendModes'; + +/** + * Utility methods for Sprite/Texture tinting. + * + * Tinting with the CanvasRenderer involves creating a new canvas to use as a texture, + * so be aware of the performance implications. + * + * @namespace PIXI.canvasUtils + * @memberof PIXI + */ +export const canvasUtils = { + /** + * Basically this method just needs a sprite and a color and tints the sprite with the given color. + * + * @memberof PIXI.canvasUtils + * @param {PIXI.Sprite} sprite - the sprite to tint + * @param {number} color - the color to use to tint the sprite with + * @return {HTMLCanvasElement} The tinted canvas + */ + getTintedCanvas: (sprite, color) => + { + const texture = sprite.texture; + + color = canvasUtils.roundColor(color); + + const stringColor = `#${(`00000${(color | 0).toString(16)}`).substr(-6)}`; + + texture.tintCache = texture.tintCache || {}; + + const cachedCanvas = texture.tintCache[stringColor]; + + let canvas; + + if (cachedCanvas) + { + if (cachedCanvas.tintId === texture._updateID) + { + return texture.tintCache[stringColor]; + } + + canvas = texture.tintCache[stringColor]; + } + else + { + canvas = canvasUtils.canvas || document.createElement('canvas'); + } + + canvasUtils.tintMethod(texture, color, canvas); + + canvas.tintId = texture._updateID; + + if (canvasUtils.convertTintToImage) + { + // is this better? + const tintImage = new Image(); + + tintImage.src = canvas.toDataURL(); + + texture.tintCache[stringColor] = tintImage; + } + else + { + texture.tintCache[stringColor] = canvas; + // if we are not converting the texture to an image then we need to lose the reference to the canvas + canvasUtils.canvas = null; + } + + return canvas; + }, + + /** + * Tint a texture using the 'multiply' operation. + * + * @memberof PIXI.canvasUtils + * @param {PIXI.Texture} texture - the texture to tint + * @param {number} color - the color to use to tint the sprite with + * @param {HTMLCanvasElement} canvas - the current canvas + */ + tintWithMultiply: (texture, color, canvas) => + { + const context = canvas.getContext('2d'); + const crop = texture._frame.clone(); + const resolution = texture.baseTexture.resolution; + + crop.x *= resolution; + crop.y *= resolution; + crop.width *= resolution; + crop.height *= resolution; + + canvas.width = Math.ceil(crop.width); + canvas.height = Math.ceil(crop.height); + + context.save(); + context.fillStyle = `#${(`00000${(color | 0).toString(16)}`).substr(-6)}`; + + context.fillRect(0, 0, crop.width, crop.height); + + context.globalCompositeOperation = 'multiply'; + + const source = texture.baseTexture.getDrawableSource(); + + context.drawImage( + source, + crop.x, + crop.y, + crop.width, + crop.height, + 0, + 0, + crop.width, + crop.height + ); + + context.globalCompositeOperation = 'destination-atop'; + + context.drawImage( + source, + crop.x, + crop.y, + crop.width, + crop.height, + 0, + 0, + crop.width, + crop.height + ); + context.restore(); + }, + + /** + * Tint a texture using the 'overlay' operation. + * + * @memberof PIXI.canvasUtils + * @param {PIXI.Texture} texture - the texture to tint + * @param {number} color - the color to use to tint the sprite with + * @param {HTMLCanvasElement} canvas - the current canvas + */ + tintWithOverlay(texture, color, canvas) + { + const context = canvas.getContext('2d'); + const crop = texture._frame.clone(); + const resolution = texture.baseTexture.resolution; + + crop.x *= resolution; + crop.y *= resolution; + crop.width *= resolution; + crop.height *= resolution; + + canvas.width = Math.ceil(crop.width); + canvas.height = Math.ceil(crop.height); + + context.save(); + context.globalCompositeOperation = 'copy'; + context.fillStyle = `#${(`00000${(color | 0).toString(16)}`).substr(-6)}`; + context.fillRect(0, 0, crop.width, crop.height); + + context.globalCompositeOperation = 'destination-atop'; + context.drawImage( + texture.baseTexture.getDrawableSource(), + crop.x, + crop.y, + crop.width, + crop.height, + 0, + 0, + crop.width, + crop.height + ); + + // context.globalCompositeOperation = 'copy'; + context.restore(); + }, + + /** + * Tint a texture pixel per pixel. + * + * @memberof PIXI.canvasUtils + * @param {PIXI.Texture} texture - the texture to tint + * @param {number} color - the color to use to tint the sprite with + * @param {HTMLCanvasElement} canvas - the current canvas + */ + tintWithPerPixel: (texture, color, canvas) => + { + const context = canvas.getContext('2d'); + const crop = texture._frame.clone(); + const resolution = texture.baseTexture.resolution; + + crop.x *= resolution; + crop.y *= resolution; + crop.width *= resolution; + crop.height *= resolution; + + canvas.width = Math.ceil(crop.width); + canvas.height = Math.ceil(crop.height); + + context.save(); + context.globalCompositeOperation = 'copy'; + context.drawImage( + texture.baseTexture.getDrawableSource(), + crop.x, + crop.y, + crop.width, + crop.height, + 0, + 0, + crop.width, + crop.height + ); + context.restore(); + + const rgbValues = hex2rgb(color); + const r = rgbValues[0]; + const g = rgbValues[1]; + const b = rgbValues[2]; + + const pixelData = context.getImageData(0, 0, crop.width, crop.height); + + const pixels = pixelData.data; + + for (let i = 0; i < pixels.length; i += 4) + { + pixels[i + 0] *= r; + pixels[i + 1] *= g; + pixels[i + 2] *= b; + } + + context.putImageData(pixelData, 0, 0); + }, + + /** + * Rounds the specified color according to the canvasUtils.cacheStepsPerColorChannel. + * + * @memberof PIXI.canvasUtils + * @param {number} color - the color to round, should be a hex color + * @return {number} The rounded color. + */ + roundColor: (color) => + { + const step = canvasUtils.cacheStepsPerColorChannel; + + const rgbValues = hex2rgb(color); + + rgbValues[0] = Math.min(255, (rgbValues[0] / step) * step); + rgbValues[1] = Math.min(255, (rgbValues[1] / step) * step); + rgbValues[2] = Math.min(255, (rgbValues[2] / step) * step); + + return rgb2hex(rgbValues); + }, + + /** + * Number of steps which will be used as a cap when rounding colors. + * + * @memberof PIXI.canvasUtils + * @type {number} + */ + cacheStepsPerColorChannel: 8, + + /** + * Tint cache boolean flag. + * + * @memberof PIXI.canvasUtils + * @type {boolean} + */ + convertTintToImage: false, + + /** + * Whether or not the Canvas BlendModes are supported, consequently the ability to tint using the multiply method. + * + * @memberof PIXI.canvasUtils + * @type {boolean} + */ + canUseMultiply: canUseNewCanvasBlendModes(), + + /** + * The tinting method that will be used. + * + * @memberof PIXI.canvasUtils + * @type {Function} + */ + tintMethod: () => + { // jslint-disable no-empty-function + + }, +}; + +canvasUtils.tintMethod = canvasUtils.canUseMultiply ? canvasUtils.tintWithMultiply : canvasUtils.tintWithPerPixel; diff --git a/packages/canvas/canvas-renderer/src/index.js b/packages/canvas/canvas-renderer/src/index.js index 80987c3..71bc5ee 100644 --- a/packages/canvas/canvas-renderer/src/index.js +++ b/packages/canvas/canvas-renderer/src/index.js @@ -1,6 +1,6 @@ export * from './CanvasRenderer'; export * from './utils/canUseNewCanvasBlendModes'; -export * from './CanvasTinter'; +export * from './canvasUtils'; import './Renderer'; import './BaseTexture'; diff --git a/packages/canvas/canvas-sprite-tiling/src/TilingSprite.js b/packages/canvas/canvas-sprite-tiling/src/TilingSprite.js index 4eac1d7..fd8857f 100644 --- a/packages/canvas/canvas-sprite-tiling/src/TilingSprite.js +++ b/packages/canvas/canvas-sprite-tiling/src/TilingSprite.js @@ -1,5 +1,5 @@ import { TilingSprite } from '@pixi/sprite-tiling'; -import { CanvasTinter } from '@pixi/canvas-renderer'; +import { canvasUtils } from '@pixi/canvas-renderer'; import { CanvasRenderTarget } from '@pixi/utils'; /** @@ -40,7 +40,7 @@ // Tint the tiling sprite if (this.tint !== 0xFFFFFF) { - this._tintedCanvas = CanvasTinter.getTintedCanvas(this, this.tint); + this._tintedCanvas = canvasUtils.getTintedCanvas(this, this.tint); tempCanvas.context.drawImage(this._tintedCanvas, 0, 0); } else diff --git a/packages/canvas/canvas-sprite/src/CanvasSpriteRenderer.js b/packages/canvas/canvas-sprite/src/CanvasSpriteRenderer.js index 507571c..3cdea2a 100644 --- a/packages/canvas/canvas-sprite/src/CanvasSpriteRenderer.js +++ b/packages/canvas/canvas-sprite/src/CanvasSpriteRenderer.js @@ -1,6 +1,6 @@ import { SCALE_MODES, BLEND_MODES } from '@pixi/constants'; -import { Matrix, GroupD8 } from '@pixi/math'; -import { CanvasTinter } from '@pixi/canvas-renderer'; +import { Matrix, groupD8 } from '@pixi/math'; +import { canvasUtils } from '@pixi/canvas-renderer'; const canvasRenderWorldTransform = new Matrix(); @@ -96,7 +96,7 @@ { wt.copyTo(canvasRenderWorldTransform); wt = canvasRenderWorldTransform; - GroupD8.matrixAppendRotationInv(wt, texture.rotate, dx, dy); + groupD8.matrixAppendRotationInv(wt, texture.rotate, dx, dy); // the anchor has already been applied above, so lets set it to zero dx = 0; dy = 0; @@ -155,7 +155,7 @@ sprite._cachedTint = sprite.tint; // TODO clean up caching - how to clean up the caches? - sprite._tintedCanvas = CanvasTinter.getTintedCanvas(sprite, sprite.tint); + sprite._tintedCanvas = canvasUtils.getTintedCanvas(sprite, sprite.tint); } context.drawImage( diff --git a/bundles/pixi.js-legacy/src/index.js b/bundles/pixi.js-legacy/src/index.js index f1598a1..df6b157 100644 --- a/bundles/pixi.js-legacy/src/index.js +++ b/bundles/pixi.js-legacy/src/index.js @@ -1,5 +1,5 @@ import { accessibility, interaction, prepare, extract } from 'pixi.js'; -import { CanvasRenderer, CanvasTinter } from '@pixi/canvas-renderer'; +import { CanvasRenderer, canvasUtils } from '@pixi/canvas-renderer'; import { CanvasMeshRenderer } from '@pixi/canvas-mesh'; import { CanvasGraphicsRenderer } from '@pixi/canvas-graphics'; import { CanvasSpriteRenderer } from '@pixi/canvas-sprite'; @@ -29,5 +29,5 @@ CanvasGraphicsRenderer, CanvasMeshRenderer, CanvasSpriteRenderer, - CanvasTinter, + canvasUtils, }; diff --git a/bundles/pixi.js/src/useDeprecated.js b/bundles/pixi.js/src/useDeprecated.js index 12f00d4..ab88092 100644 --- a/bundles/pixi.js/src/useDeprecated.js +++ b/bundles/pixi.js/src/useDeprecated.js @@ -138,6 +138,34 @@ return PIXI.systems.FilterSystem; }, }, + + /** + * @namespace PIXI.CanvasTinter + * @see PIXI.canvasUtils + * @deprecated since 5.2.0 + */ + CanvasTinter: { + get() + { + deprecation('5.2.0', 'PIXI.CanvasTinter namespace has moved to PIXI.canvasUtils'); + + return PIXI.canvasUtils; + }, + }, + + /** + * @namespace PIXI.GroupD8 + * @see PIXI.groupD8 + * @deprecated since 5.2.0 + */ + GroupD8: { + get() + { + deprecation('5.2.0', 'PIXI.GroupD8 namespace has moved to PIXI.groupD8'); + + return PIXI.groupD8; + }, + }, }); /** diff --git a/packages/canvas/canvas-mesh/src/NineSlicePlane.js b/packages/canvas/canvas-mesh/src/NineSlicePlane.js index dccfd79..6e2f055 100644 --- a/packages/canvas/canvas-mesh/src/NineSlicePlane.js +++ b/packages/canvas/canvas-mesh/src/NineSlicePlane.js @@ -1,4 +1,4 @@ -import { CanvasTinter } from '@pixi/canvas-renderer'; +import { canvasUtils } from '@pixi/canvas-renderer'; import { NineSlicePlane } from '@pixi/mesh-extras'; /** @@ -50,7 +50,7 @@ this._cachedTint = this.tint; - this._tintedCanvas = CanvasTinter.getTintedCanvas(this, this.tint); + this._tintedCanvas = canvasUtils.getTintedCanvas(this, this.tint); } } diff --git a/packages/canvas/canvas-renderer/src/CanvasTinter.js b/packages/canvas/canvas-renderer/src/CanvasTinter.js deleted file mode 100644 index bfec51a..0000000 --- a/packages/canvas/canvas-renderer/src/CanvasTinter.js +++ /dev/null @@ -1,288 +0,0 @@ -import { hex2rgb, rgb2hex } from '@pixi/utils'; -import { canUseNewCanvasBlendModes } from './utils/canUseNewCanvasBlendModes'; - -/** - * Utility methods for Sprite/Texture tinting. - * - * Tinting with the CanvasRenderer involves creating a new canvas to use as a texture, - * so be aware of the performance implications. - * - * @namespace PIXI.CanvasTinter - * @memberof PIXI - */ -export const CanvasTinter = { - /** - * Basically this method just needs a sprite and a color and tints the sprite with the given color. - * - * @memberof PIXI.CanvasTinter - * @param {PIXI.Sprite} sprite - the sprite to tint - * @param {number} color - the color to use to tint the sprite with - * @return {HTMLCanvasElement} The tinted canvas - */ - getTintedCanvas: (sprite, color) => - { - const texture = sprite.texture; - - color = CanvasTinter.roundColor(color); - - const stringColor = `#${(`00000${(color | 0).toString(16)}`).substr(-6)}`; - - texture.tintCache = texture.tintCache || {}; - - const cachedCanvas = texture.tintCache[stringColor]; - - let canvas; - - if (cachedCanvas) - { - if (cachedCanvas.tintId === texture._updateID) - { - return texture.tintCache[stringColor]; - } - - canvas = texture.tintCache[stringColor]; - } - else - { - canvas = CanvasTinter.canvas || document.createElement('canvas'); - } - - CanvasTinter.tintMethod(texture, color, canvas); - - canvas.tintId = texture._updateID; - - if (CanvasTinter.convertTintToImage) - { - // is this better? - const tintImage = new Image(); - - tintImage.src = canvas.toDataURL(); - - texture.tintCache[stringColor] = tintImage; - } - else - { - texture.tintCache[stringColor] = canvas; - // if we are not converting the texture to an image then we need to lose the reference to the canvas - CanvasTinter.canvas = null; - } - - return canvas; - }, - - /** - * Tint a texture using the 'multiply' operation. - * - * @memberof PIXI.CanvasTinter - * @param {PIXI.Texture} texture - the texture to tint - * @param {number} color - the color to use to tint the sprite with - * @param {HTMLCanvasElement} canvas - the current canvas - */ - tintWithMultiply: (texture, color, canvas) => - { - const context = canvas.getContext('2d'); - const crop = texture._frame.clone(); - const resolution = texture.baseTexture.resolution; - - crop.x *= resolution; - crop.y *= resolution; - crop.width *= resolution; - crop.height *= resolution; - - canvas.width = Math.ceil(crop.width); - canvas.height = Math.ceil(crop.height); - - context.save(); - context.fillStyle = `#${(`00000${(color | 0).toString(16)}`).substr(-6)}`; - - context.fillRect(0, 0, crop.width, crop.height); - - context.globalCompositeOperation = 'multiply'; - - const source = texture.baseTexture.getDrawableSource(); - - context.drawImage( - source, - crop.x, - crop.y, - crop.width, - crop.height, - 0, - 0, - crop.width, - crop.height - ); - - context.globalCompositeOperation = 'destination-atop'; - - context.drawImage( - source, - crop.x, - crop.y, - crop.width, - crop.height, - 0, - 0, - crop.width, - crop.height - ); - context.restore(); - }, - - /** - * Tint a texture using the 'overlay' operation. - * - * @memberof PIXI.CanvasTinter - * @param {PIXI.Texture} texture - the texture to tint - * @param {number} color - the color to use to tint the sprite with - * @param {HTMLCanvasElement} canvas - the current canvas - */ - tintWithOverlay(texture, color, canvas) - { - const context = canvas.getContext('2d'); - const crop = texture._frame.clone(); - const resolution = texture.baseTexture.resolution; - - crop.x *= resolution; - crop.y *= resolution; - crop.width *= resolution; - crop.height *= resolution; - - canvas.width = Math.ceil(crop.width); - canvas.height = Math.ceil(crop.height); - - context.save(); - context.globalCompositeOperation = 'copy'; - context.fillStyle = `#${(`00000${(color | 0).toString(16)}`).substr(-6)}`; - context.fillRect(0, 0, crop.width, crop.height); - - context.globalCompositeOperation = 'destination-atop'; - context.drawImage( - texture.baseTexture.getDrawableSource(), - crop.x, - crop.y, - crop.width, - crop.height, - 0, - 0, - crop.width, - crop.height - ); - - // context.globalCompositeOperation = 'copy'; - context.restore(); - }, - - /** - * Tint a texture pixel per pixel. - * - * @memberof PIXI.CanvasTinter - * @param {PIXI.Texture} texture - the texture to tint - * @param {number} color - the color to use to tint the sprite with - * @param {HTMLCanvasElement} canvas - the current canvas - */ - tintWithPerPixel: (texture, color, canvas) => - { - const context = canvas.getContext('2d'); - const crop = texture._frame.clone(); - const resolution = texture.baseTexture.resolution; - - crop.x *= resolution; - crop.y *= resolution; - crop.width *= resolution; - crop.height *= resolution; - - canvas.width = Math.ceil(crop.width); - canvas.height = Math.ceil(crop.height); - - context.save(); - context.globalCompositeOperation = 'copy'; - context.drawImage( - texture.baseTexture.getDrawableSource(), - crop.x, - crop.y, - crop.width, - crop.height, - 0, - 0, - crop.width, - crop.height - ); - context.restore(); - - const rgbValues = hex2rgb(color); - const r = rgbValues[0]; - const g = rgbValues[1]; - const b = rgbValues[2]; - - const pixelData = context.getImageData(0, 0, crop.width, crop.height); - - const pixels = pixelData.data; - - for (let i = 0; i < pixels.length; i += 4) - { - pixels[i + 0] *= r; - pixels[i + 1] *= g; - pixels[i + 2] *= b; - } - - context.putImageData(pixelData, 0, 0); - }, - - /** - * Rounds the specified color according to the CanvasTinter.cacheStepsPerColorChannel. - * - * @memberof PIXI.CanvasTinter - * @param {number} color - the color to round, should be a hex color - * @return {number} The rounded color. - */ - roundColor: (color) => - { - const step = CanvasTinter.cacheStepsPerColorChannel; - - const rgbValues = hex2rgb(color); - - rgbValues[0] = Math.min(255, (rgbValues[0] / step) * step); - rgbValues[1] = Math.min(255, (rgbValues[1] / step) * step); - rgbValues[2] = Math.min(255, (rgbValues[2] / step) * step); - - return rgb2hex(rgbValues); - }, - - /** - * Number of steps which will be used as a cap when rounding colors. - * - * @memberof PIXI.CanvasTinter - * @type {number} - */ - cacheStepsPerColorChannel: 8, - - /** - * Tint cache boolean flag. - * - * @memberof PIXI.CanvasTinter - * @type {boolean} - */ - convertTintToImage: false, - - /** - * Whether or not the Canvas BlendModes are supported, consequently the ability to tint using the multiply method. - * - * @memberof PIXI.CanvasTinter - * @type {boolean} - */ - canUseMultiply: canUseNewCanvasBlendModes(), - - /** - * The tinting method that will be used. - * - * @memberof PIXI.CanvasTinter - * @type {Function} - */ - tintMethod: () => - { // jslint-disable no-empty-function - - }, -}; - -CanvasTinter.tintMethod = CanvasTinter.canUseMultiply ? CanvasTinter.tintWithMultiply : CanvasTinter.tintWithPerPixel; diff --git a/packages/canvas/canvas-renderer/src/canvasUtils.js b/packages/canvas/canvas-renderer/src/canvasUtils.js new file mode 100644 index 0000000..62e16d4 --- /dev/null +++ b/packages/canvas/canvas-renderer/src/canvasUtils.js @@ -0,0 +1,288 @@ +import { hex2rgb, rgb2hex } from '@pixi/utils'; +import { canUseNewCanvasBlendModes } from './utils/canUseNewCanvasBlendModes'; + +/** + * Utility methods for Sprite/Texture tinting. + * + * Tinting with the CanvasRenderer involves creating a new canvas to use as a texture, + * so be aware of the performance implications. + * + * @namespace PIXI.canvasUtils + * @memberof PIXI + */ +export const canvasUtils = { + /** + * Basically this method just needs a sprite and a color and tints the sprite with the given color. + * + * @memberof PIXI.canvasUtils + * @param {PIXI.Sprite} sprite - the sprite to tint + * @param {number} color - the color to use to tint the sprite with + * @return {HTMLCanvasElement} The tinted canvas + */ + getTintedCanvas: (sprite, color) => + { + const texture = sprite.texture; + + color = canvasUtils.roundColor(color); + + const stringColor = `#${(`00000${(color | 0).toString(16)}`).substr(-6)}`; + + texture.tintCache = texture.tintCache || {}; + + const cachedCanvas = texture.tintCache[stringColor]; + + let canvas; + + if (cachedCanvas) + { + if (cachedCanvas.tintId === texture._updateID) + { + return texture.tintCache[stringColor]; + } + + canvas = texture.tintCache[stringColor]; + } + else + { + canvas = canvasUtils.canvas || document.createElement('canvas'); + } + + canvasUtils.tintMethod(texture, color, canvas); + + canvas.tintId = texture._updateID; + + if (canvasUtils.convertTintToImage) + { + // is this better? + const tintImage = new Image(); + + tintImage.src = canvas.toDataURL(); + + texture.tintCache[stringColor] = tintImage; + } + else + { + texture.tintCache[stringColor] = canvas; + // if we are not converting the texture to an image then we need to lose the reference to the canvas + canvasUtils.canvas = null; + } + + return canvas; + }, + + /** + * Tint a texture using the 'multiply' operation. + * + * @memberof PIXI.canvasUtils + * @param {PIXI.Texture} texture - the texture to tint + * @param {number} color - the color to use to tint the sprite with + * @param {HTMLCanvasElement} canvas - the current canvas + */ + tintWithMultiply: (texture, color, canvas) => + { + const context = canvas.getContext('2d'); + const crop = texture._frame.clone(); + const resolution = texture.baseTexture.resolution; + + crop.x *= resolution; + crop.y *= resolution; + crop.width *= resolution; + crop.height *= resolution; + + canvas.width = Math.ceil(crop.width); + canvas.height = Math.ceil(crop.height); + + context.save(); + context.fillStyle = `#${(`00000${(color | 0).toString(16)}`).substr(-6)}`; + + context.fillRect(0, 0, crop.width, crop.height); + + context.globalCompositeOperation = 'multiply'; + + const source = texture.baseTexture.getDrawableSource(); + + context.drawImage( + source, + crop.x, + crop.y, + crop.width, + crop.height, + 0, + 0, + crop.width, + crop.height + ); + + context.globalCompositeOperation = 'destination-atop'; + + context.drawImage( + source, + crop.x, + crop.y, + crop.width, + crop.height, + 0, + 0, + crop.width, + crop.height + ); + context.restore(); + }, + + /** + * Tint a texture using the 'overlay' operation. + * + * @memberof PIXI.canvasUtils + * @param {PIXI.Texture} texture - the texture to tint + * @param {number} color - the color to use to tint the sprite with + * @param {HTMLCanvasElement} canvas - the current canvas + */ + tintWithOverlay(texture, color, canvas) + { + const context = canvas.getContext('2d'); + const crop = texture._frame.clone(); + const resolution = texture.baseTexture.resolution; + + crop.x *= resolution; + crop.y *= resolution; + crop.width *= resolution; + crop.height *= resolution; + + canvas.width = Math.ceil(crop.width); + canvas.height = Math.ceil(crop.height); + + context.save(); + context.globalCompositeOperation = 'copy'; + context.fillStyle = `#${(`00000${(color | 0).toString(16)}`).substr(-6)}`; + context.fillRect(0, 0, crop.width, crop.height); + + context.globalCompositeOperation = 'destination-atop'; + context.drawImage( + texture.baseTexture.getDrawableSource(), + crop.x, + crop.y, + crop.width, + crop.height, + 0, + 0, + crop.width, + crop.height + ); + + // context.globalCompositeOperation = 'copy'; + context.restore(); + }, + + /** + * Tint a texture pixel per pixel. + * + * @memberof PIXI.canvasUtils + * @param {PIXI.Texture} texture - the texture to tint + * @param {number} color - the color to use to tint the sprite with + * @param {HTMLCanvasElement} canvas - the current canvas + */ + tintWithPerPixel: (texture, color, canvas) => + { + const context = canvas.getContext('2d'); + const crop = texture._frame.clone(); + const resolution = texture.baseTexture.resolution; + + crop.x *= resolution; + crop.y *= resolution; + crop.width *= resolution; + crop.height *= resolution; + + canvas.width = Math.ceil(crop.width); + canvas.height = Math.ceil(crop.height); + + context.save(); + context.globalCompositeOperation = 'copy'; + context.drawImage( + texture.baseTexture.getDrawableSource(), + crop.x, + crop.y, + crop.width, + crop.height, + 0, + 0, + crop.width, + crop.height + ); + context.restore(); + + const rgbValues = hex2rgb(color); + const r = rgbValues[0]; + const g = rgbValues[1]; + const b = rgbValues[2]; + + const pixelData = context.getImageData(0, 0, crop.width, crop.height); + + const pixels = pixelData.data; + + for (let i = 0; i < pixels.length; i += 4) + { + pixels[i + 0] *= r; + pixels[i + 1] *= g; + pixels[i + 2] *= b; + } + + context.putImageData(pixelData, 0, 0); + }, + + /** + * Rounds the specified color according to the canvasUtils.cacheStepsPerColorChannel. + * + * @memberof PIXI.canvasUtils + * @param {number} color - the color to round, should be a hex color + * @return {number} The rounded color. + */ + roundColor: (color) => + { + const step = canvasUtils.cacheStepsPerColorChannel; + + const rgbValues = hex2rgb(color); + + rgbValues[0] = Math.min(255, (rgbValues[0] / step) * step); + rgbValues[1] = Math.min(255, (rgbValues[1] / step) * step); + rgbValues[2] = Math.min(255, (rgbValues[2] / step) * step); + + return rgb2hex(rgbValues); + }, + + /** + * Number of steps which will be used as a cap when rounding colors. + * + * @memberof PIXI.canvasUtils + * @type {number} + */ + cacheStepsPerColorChannel: 8, + + /** + * Tint cache boolean flag. + * + * @memberof PIXI.canvasUtils + * @type {boolean} + */ + convertTintToImage: false, + + /** + * Whether or not the Canvas BlendModes are supported, consequently the ability to tint using the multiply method. + * + * @memberof PIXI.canvasUtils + * @type {boolean} + */ + canUseMultiply: canUseNewCanvasBlendModes(), + + /** + * The tinting method that will be used. + * + * @memberof PIXI.canvasUtils + * @type {Function} + */ + tintMethod: () => + { // jslint-disable no-empty-function + + }, +}; + +canvasUtils.tintMethod = canvasUtils.canUseMultiply ? canvasUtils.tintWithMultiply : canvasUtils.tintWithPerPixel; diff --git a/packages/canvas/canvas-renderer/src/index.js b/packages/canvas/canvas-renderer/src/index.js index 80987c3..71bc5ee 100644 --- a/packages/canvas/canvas-renderer/src/index.js +++ b/packages/canvas/canvas-renderer/src/index.js @@ -1,6 +1,6 @@ export * from './CanvasRenderer'; export * from './utils/canUseNewCanvasBlendModes'; -export * from './CanvasTinter'; +export * from './canvasUtils'; import './Renderer'; import './BaseTexture'; diff --git a/packages/canvas/canvas-sprite-tiling/src/TilingSprite.js b/packages/canvas/canvas-sprite-tiling/src/TilingSprite.js index 4eac1d7..fd8857f 100644 --- a/packages/canvas/canvas-sprite-tiling/src/TilingSprite.js +++ b/packages/canvas/canvas-sprite-tiling/src/TilingSprite.js @@ -1,5 +1,5 @@ import { TilingSprite } from '@pixi/sprite-tiling'; -import { CanvasTinter } from '@pixi/canvas-renderer'; +import { canvasUtils } from '@pixi/canvas-renderer'; import { CanvasRenderTarget } from '@pixi/utils'; /** @@ -40,7 +40,7 @@ // Tint the tiling sprite if (this.tint !== 0xFFFFFF) { - this._tintedCanvas = CanvasTinter.getTintedCanvas(this, this.tint); + this._tintedCanvas = canvasUtils.getTintedCanvas(this, this.tint); tempCanvas.context.drawImage(this._tintedCanvas, 0, 0); } else diff --git a/packages/canvas/canvas-sprite/src/CanvasSpriteRenderer.js b/packages/canvas/canvas-sprite/src/CanvasSpriteRenderer.js index 507571c..3cdea2a 100644 --- a/packages/canvas/canvas-sprite/src/CanvasSpriteRenderer.js +++ b/packages/canvas/canvas-sprite/src/CanvasSpriteRenderer.js @@ -1,6 +1,6 @@ import { SCALE_MODES, BLEND_MODES } from '@pixi/constants'; -import { Matrix, GroupD8 } from '@pixi/math'; -import { CanvasTinter } from '@pixi/canvas-renderer'; +import { Matrix, groupD8 } from '@pixi/math'; +import { canvasUtils } from '@pixi/canvas-renderer'; const canvasRenderWorldTransform = new Matrix(); @@ -96,7 +96,7 @@ { wt.copyTo(canvasRenderWorldTransform); wt = canvasRenderWorldTransform; - GroupD8.matrixAppendRotationInv(wt, texture.rotate, dx, dy); + groupD8.matrixAppendRotationInv(wt, texture.rotate, dx, dy); // the anchor has already been applied above, so lets set it to zero dx = 0; dy = 0; @@ -155,7 +155,7 @@ sprite._cachedTint = sprite.tint; // TODO clean up caching - how to clean up the caches? - sprite._tintedCanvas = CanvasTinter.getTintedCanvas(sprite, sprite.tint); + sprite._tintedCanvas = canvasUtils.getTintedCanvas(sprite, sprite.tint); } context.drawImage( diff --git a/packages/core/src/textures/Texture.js b/packages/core/src/textures/Texture.js index ea5c843..107b61e 100644 --- a/packages/core/src/textures/Texture.js +++ b/packages/core/src/textures/Texture.js @@ -45,7 +45,7 @@ * @param {PIXI.Rectangle} [frame] - The rectangle frame of the texture to show * @param {PIXI.Rectangle} [orig] - The area of original texture * @param {PIXI.Rectangle} [trim] - Trimmed rectangle of original texture - * @param {number} [rotate] - indicates how the texture was rotated by texture packer. See {@link PIXI.GroupD8} + * @param {number} [rotate] - indicates how the texture was rotated by texture packer. See {@link PIXI.groupD8} * @param {PIXI.Point} [anchor] - Default anchor point used for sprite placement / rotation */ constructor(baseTexture, frame, orig, trim, rotate, anchor) @@ -563,7 +563,7 @@ * set to 2 to compensate for texture packer rotation * set to 6 to compensate for spine packer rotation * can be used to rotate or mirror sprites - * See {@link PIXI.GroupD8} for explanation + * See {@link PIXI.groupD8} for explanation * * @member {number} */ diff --git a/bundles/pixi.js-legacy/src/index.js b/bundles/pixi.js-legacy/src/index.js index f1598a1..df6b157 100644 --- a/bundles/pixi.js-legacy/src/index.js +++ b/bundles/pixi.js-legacy/src/index.js @@ -1,5 +1,5 @@ import { accessibility, interaction, prepare, extract } from 'pixi.js'; -import { CanvasRenderer, CanvasTinter } from '@pixi/canvas-renderer'; +import { CanvasRenderer, canvasUtils } from '@pixi/canvas-renderer'; import { CanvasMeshRenderer } from '@pixi/canvas-mesh'; import { CanvasGraphicsRenderer } from '@pixi/canvas-graphics'; import { CanvasSpriteRenderer } from '@pixi/canvas-sprite'; @@ -29,5 +29,5 @@ CanvasGraphicsRenderer, CanvasMeshRenderer, CanvasSpriteRenderer, - CanvasTinter, + canvasUtils, }; diff --git a/bundles/pixi.js/src/useDeprecated.js b/bundles/pixi.js/src/useDeprecated.js index 12f00d4..ab88092 100644 --- a/bundles/pixi.js/src/useDeprecated.js +++ b/bundles/pixi.js/src/useDeprecated.js @@ -138,6 +138,34 @@ return PIXI.systems.FilterSystem; }, }, + + /** + * @namespace PIXI.CanvasTinter + * @see PIXI.canvasUtils + * @deprecated since 5.2.0 + */ + CanvasTinter: { + get() + { + deprecation('5.2.0', 'PIXI.CanvasTinter namespace has moved to PIXI.canvasUtils'); + + return PIXI.canvasUtils; + }, + }, + + /** + * @namespace PIXI.GroupD8 + * @see PIXI.groupD8 + * @deprecated since 5.2.0 + */ + GroupD8: { + get() + { + deprecation('5.2.0', 'PIXI.GroupD8 namespace has moved to PIXI.groupD8'); + + return PIXI.groupD8; + }, + }, }); /** diff --git a/packages/canvas/canvas-mesh/src/NineSlicePlane.js b/packages/canvas/canvas-mesh/src/NineSlicePlane.js index dccfd79..6e2f055 100644 --- a/packages/canvas/canvas-mesh/src/NineSlicePlane.js +++ b/packages/canvas/canvas-mesh/src/NineSlicePlane.js @@ -1,4 +1,4 @@ -import { CanvasTinter } from '@pixi/canvas-renderer'; +import { canvasUtils } from '@pixi/canvas-renderer'; import { NineSlicePlane } from '@pixi/mesh-extras'; /** @@ -50,7 +50,7 @@ this._cachedTint = this.tint; - this._tintedCanvas = CanvasTinter.getTintedCanvas(this, this.tint); + this._tintedCanvas = canvasUtils.getTintedCanvas(this, this.tint); } } diff --git a/packages/canvas/canvas-renderer/src/CanvasTinter.js b/packages/canvas/canvas-renderer/src/CanvasTinter.js deleted file mode 100644 index bfec51a..0000000 --- a/packages/canvas/canvas-renderer/src/CanvasTinter.js +++ /dev/null @@ -1,288 +0,0 @@ -import { hex2rgb, rgb2hex } from '@pixi/utils'; -import { canUseNewCanvasBlendModes } from './utils/canUseNewCanvasBlendModes'; - -/** - * Utility methods for Sprite/Texture tinting. - * - * Tinting with the CanvasRenderer involves creating a new canvas to use as a texture, - * so be aware of the performance implications. - * - * @namespace PIXI.CanvasTinter - * @memberof PIXI - */ -export const CanvasTinter = { - /** - * Basically this method just needs a sprite and a color and tints the sprite with the given color. - * - * @memberof PIXI.CanvasTinter - * @param {PIXI.Sprite} sprite - the sprite to tint - * @param {number} color - the color to use to tint the sprite with - * @return {HTMLCanvasElement} The tinted canvas - */ - getTintedCanvas: (sprite, color) => - { - const texture = sprite.texture; - - color = CanvasTinter.roundColor(color); - - const stringColor = `#${(`00000${(color | 0).toString(16)}`).substr(-6)}`; - - texture.tintCache = texture.tintCache || {}; - - const cachedCanvas = texture.tintCache[stringColor]; - - let canvas; - - if (cachedCanvas) - { - if (cachedCanvas.tintId === texture._updateID) - { - return texture.tintCache[stringColor]; - } - - canvas = texture.tintCache[stringColor]; - } - else - { - canvas = CanvasTinter.canvas || document.createElement('canvas'); - } - - CanvasTinter.tintMethod(texture, color, canvas); - - canvas.tintId = texture._updateID; - - if (CanvasTinter.convertTintToImage) - { - // is this better? - const tintImage = new Image(); - - tintImage.src = canvas.toDataURL(); - - texture.tintCache[stringColor] = tintImage; - } - else - { - texture.tintCache[stringColor] = canvas; - // if we are not converting the texture to an image then we need to lose the reference to the canvas - CanvasTinter.canvas = null; - } - - return canvas; - }, - - /** - * Tint a texture using the 'multiply' operation. - * - * @memberof PIXI.CanvasTinter - * @param {PIXI.Texture} texture - the texture to tint - * @param {number} color - the color to use to tint the sprite with - * @param {HTMLCanvasElement} canvas - the current canvas - */ - tintWithMultiply: (texture, color, canvas) => - { - const context = canvas.getContext('2d'); - const crop = texture._frame.clone(); - const resolution = texture.baseTexture.resolution; - - crop.x *= resolution; - crop.y *= resolution; - crop.width *= resolution; - crop.height *= resolution; - - canvas.width = Math.ceil(crop.width); - canvas.height = Math.ceil(crop.height); - - context.save(); - context.fillStyle = `#${(`00000${(color | 0).toString(16)}`).substr(-6)}`; - - context.fillRect(0, 0, crop.width, crop.height); - - context.globalCompositeOperation = 'multiply'; - - const source = texture.baseTexture.getDrawableSource(); - - context.drawImage( - source, - crop.x, - crop.y, - crop.width, - crop.height, - 0, - 0, - crop.width, - crop.height - ); - - context.globalCompositeOperation = 'destination-atop'; - - context.drawImage( - source, - crop.x, - crop.y, - crop.width, - crop.height, - 0, - 0, - crop.width, - crop.height - ); - context.restore(); - }, - - /** - * Tint a texture using the 'overlay' operation. - * - * @memberof PIXI.CanvasTinter - * @param {PIXI.Texture} texture - the texture to tint - * @param {number} color - the color to use to tint the sprite with - * @param {HTMLCanvasElement} canvas - the current canvas - */ - tintWithOverlay(texture, color, canvas) - { - const context = canvas.getContext('2d'); - const crop = texture._frame.clone(); - const resolution = texture.baseTexture.resolution; - - crop.x *= resolution; - crop.y *= resolution; - crop.width *= resolution; - crop.height *= resolution; - - canvas.width = Math.ceil(crop.width); - canvas.height = Math.ceil(crop.height); - - context.save(); - context.globalCompositeOperation = 'copy'; - context.fillStyle = `#${(`00000${(color | 0).toString(16)}`).substr(-6)}`; - context.fillRect(0, 0, crop.width, crop.height); - - context.globalCompositeOperation = 'destination-atop'; - context.drawImage( - texture.baseTexture.getDrawableSource(), - crop.x, - crop.y, - crop.width, - crop.height, - 0, - 0, - crop.width, - crop.height - ); - - // context.globalCompositeOperation = 'copy'; - context.restore(); - }, - - /** - * Tint a texture pixel per pixel. - * - * @memberof PIXI.CanvasTinter - * @param {PIXI.Texture} texture - the texture to tint - * @param {number} color - the color to use to tint the sprite with - * @param {HTMLCanvasElement} canvas - the current canvas - */ - tintWithPerPixel: (texture, color, canvas) => - { - const context = canvas.getContext('2d'); - const crop = texture._frame.clone(); - const resolution = texture.baseTexture.resolution; - - crop.x *= resolution; - crop.y *= resolution; - crop.width *= resolution; - crop.height *= resolution; - - canvas.width = Math.ceil(crop.width); - canvas.height = Math.ceil(crop.height); - - context.save(); - context.globalCompositeOperation = 'copy'; - context.drawImage( - texture.baseTexture.getDrawableSource(), - crop.x, - crop.y, - crop.width, - crop.height, - 0, - 0, - crop.width, - crop.height - ); - context.restore(); - - const rgbValues = hex2rgb(color); - const r = rgbValues[0]; - const g = rgbValues[1]; - const b = rgbValues[2]; - - const pixelData = context.getImageData(0, 0, crop.width, crop.height); - - const pixels = pixelData.data; - - for (let i = 0; i < pixels.length; i += 4) - { - pixels[i + 0] *= r; - pixels[i + 1] *= g; - pixels[i + 2] *= b; - } - - context.putImageData(pixelData, 0, 0); - }, - - /** - * Rounds the specified color according to the CanvasTinter.cacheStepsPerColorChannel. - * - * @memberof PIXI.CanvasTinter - * @param {number} color - the color to round, should be a hex color - * @return {number} The rounded color. - */ - roundColor: (color) => - { - const step = CanvasTinter.cacheStepsPerColorChannel; - - const rgbValues = hex2rgb(color); - - rgbValues[0] = Math.min(255, (rgbValues[0] / step) * step); - rgbValues[1] = Math.min(255, (rgbValues[1] / step) * step); - rgbValues[2] = Math.min(255, (rgbValues[2] / step) * step); - - return rgb2hex(rgbValues); - }, - - /** - * Number of steps which will be used as a cap when rounding colors. - * - * @memberof PIXI.CanvasTinter - * @type {number} - */ - cacheStepsPerColorChannel: 8, - - /** - * Tint cache boolean flag. - * - * @memberof PIXI.CanvasTinter - * @type {boolean} - */ - convertTintToImage: false, - - /** - * Whether or not the Canvas BlendModes are supported, consequently the ability to tint using the multiply method. - * - * @memberof PIXI.CanvasTinter - * @type {boolean} - */ - canUseMultiply: canUseNewCanvasBlendModes(), - - /** - * The tinting method that will be used. - * - * @memberof PIXI.CanvasTinter - * @type {Function} - */ - tintMethod: () => - { // jslint-disable no-empty-function - - }, -}; - -CanvasTinter.tintMethod = CanvasTinter.canUseMultiply ? CanvasTinter.tintWithMultiply : CanvasTinter.tintWithPerPixel; diff --git a/packages/canvas/canvas-renderer/src/canvasUtils.js b/packages/canvas/canvas-renderer/src/canvasUtils.js new file mode 100644 index 0000000..62e16d4 --- /dev/null +++ b/packages/canvas/canvas-renderer/src/canvasUtils.js @@ -0,0 +1,288 @@ +import { hex2rgb, rgb2hex } from '@pixi/utils'; +import { canUseNewCanvasBlendModes } from './utils/canUseNewCanvasBlendModes'; + +/** + * Utility methods for Sprite/Texture tinting. + * + * Tinting with the CanvasRenderer involves creating a new canvas to use as a texture, + * so be aware of the performance implications. + * + * @namespace PIXI.canvasUtils + * @memberof PIXI + */ +export const canvasUtils = { + /** + * Basically this method just needs a sprite and a color and tints the sprite with the given color. + * + * @memberof PIXI.canvasUtils + * @param {PIXI.Sprite} sprite - the sprite to tint + * @param {number} color - the color to use to tint the sprite with + * @return {HTMLCanvasElement} The tinted canvas + */ + getTintedCanvas: (sprite, color) => + { + const texture = sprite.texture; + + color = canvasUtils.roundColor(color); + + const stringColor = `#${(`00000${(color | 0).toString(16)}`).substr(-6)}`; + + texture.tintCache = texture.tintCache || {}; + + const cachedCanvas = texture.tintCache[stringColor]; + + let canvas; + + if (cachedCanvas) + { + if (cachedCanvas.tintId === texture._updateID) + { + return texture.tintCache[stringColor]; + } + + canvas = texture.tintCache[stringColor]; + } + else + { + canvas = canvasUtils.canvas || document.createElement('canvas'); + } + + canvasUtils.tintMethod(texture, color, canvas); + + canvas.tintId = texture._updateID; + + if (canvasUtils.convertTintToImage) + { + // is this better? + const tintImage = new Image(); + + tintImage.src = canvas.toDataURL(); + + texture.tintCache[stringColor] = tintImage; + } + else + { + texture.tintCache[stringColor] = canvas; + // if we are not converting the texture to an image then we need to lose the reference to the canvas + canvasUtils.canvas = null; + } + + return canvas; + }, + + /** + * Tint a texture using the 'multiply' operation. + * + * @memberof PIXI.canvasUtils + * @param {PIXI.Texture} texture - the texture to tint + * @param {number} color - the color to use to tint the sprite with + * @param {HTMLCanvasElement} canvas - the current canvas + */ + tintWithMultiply: (texture, color, canvas) => + { + const context = canvas.getContext('2d'); + const crop = texture._frame.clone(); + const resolution = texture.baseTexture.resolution; + + crop.x *= resolution; + crop.y *= resolution; + crop.width *= resolution; + crop.height *= resolution; + + canvas.width = Math.ceil(crop.width); + canvas.height = Math.ceil(crop.height); + + context.save(); + context.fillStyle = `#${(`00000${(color | 0).toString(16)}`).substr(-6)}`; + + context.fillRect(0, 0, crop.width, crop.height); + + context.globalCompositeOperation = 'multiply'; + + const source = texture.baseTexture.getDrawableSource(); + + context.drawImage( + source, + crop.x, + crop.y, + crop.width, + crop.height, + 0, + 0, + crop.width, + crop.height + ); + + context.globalCompositeOperation = 'destination-atop'; + + context.drawImage( + source, + crop.x, + crop.y, + crop.width, + crop.height, + 0, + 0, + crop.width, + crop.height + ); + context.restore(); + }, + + /** + * Tint a texture using the 'overlay' operation. + * + * @memberof PIXI.canvasUtils + * @param {PIXI.Texture} texture - the texture to tint + * @param {number} color - the color to use to tint the sprite with + * @param {HTMLCanvasElement} canvas - the current canvas + */ + tintWithOverlay(texture, color, canvas) + { + const context = canvas.getContext('2d'); + const crop = texture._frame.clone(); + const resolution = texture.baseTexture.resolution; + + crop.x *= resolution; + crop.y *= resolution; + crop.width *= resolution; + crop.height *= resolution; + + canvas.width = Math.ceil(crop.width); + canvas.height = Math.ceil(crop.height); + + context.save(); + context.globalCompositeOperation = 'copy'; + context.fillStyle = `#${(`00000${(color | 0).toString(16)}`).substr(-6)}`; + context.fillRect(0, 0, crop.width, crop.height); + + context.globalCompositeOperation = 'destination-atop'; + context.drawImage( + texture.baseTexture.getDrawableSource(), + crop.x, + crop.y, + crop.width, + crop.height, + 0, + 0, + crop.width, + crop.height + ); + + // context.globalCompositeOperation = 'copy'; + context.restore(); + }, + + /** + * Tint a texture pixel per pixel. + * + * @memberof PIXI.canvasUtils + * @param {PIXI.Texture} texture - the texture to tint + * @param {number} color - the color to use to tint the sprite with + * @param {HTMLCanvasElement} canvas - the current canvas + */ + tintWithPerPixel: (texture, color, canvas) => + { + const context = canvas.getContext('2d'); + const crop = texture._frame.clone(); + const resolution = texture.baseTexture.resolution; + + crop.x *= resolution; + crop.y *= resolution; + crop.width *= resolution; + crop.height *= resolution; + + canvas.width = Math.ceil(crop.width); + canvas.height = Math.ceil(crop.height); + + context.save(); + context.globalCompositeOperation = 'copy'; + context.drawImage( + texture.baseTexture.getDrawableSource(), + crop.x, + crop.y, + crop.width, + crop.height, + 0, + 0, + crop.width, + crop.height + ); + context.restore(); + + const rgbValues = hex2rgb(color); + const r = rgbValues[0]; + const g = rgbValues[1]; + const b = rgbValues[2]; + + const pixelData = context.getImageData(0, 0, crop.width, crop.height); + + const pixels = pixelData.data; + + for (let i = 0; i < pixels.length; i += 4) + { + pixels[i + 0] *= r; + pixels[i + 1] *= g; + pixels[i + 2] *= b; + } + + context.putImageData(pixelData, 0, 0); + }, + + /** + * Rounds the specified color according to the canvasUtils.cacheStepsPerColorChannel. + * + * @memberof PIXI.canvasUtils + * @param {number} color - the color to round, should be a hex color + * @return {number} The rounded color. + */ + roundColor: (color) => + { + const step = canvasUtils.cacheStepsPerColorChannel; + + const rgbValues = hex2rgb(color); + + rgbValues[0] = Math.min(255, (rgbValues[0] / step) * step); + rgbValues[1] = Math.min(255, (rgbValues[1] / step) * step); + rgbValues[2] = Math.min(255, (rgbValues[2] / step) * step); + + return rgb2hex(rgbValues); + }, + + /** + * Number of steps which will be used as a cap when rounding colors. + * + * @memberof PIXI.canvasUtils + * @type {number} + */ + cacheStepsPerColorChannel: 8, + + /** + * Tint cache boolean flag. + * + * @memberof PIXI.canvasUtils + * @type {boolean} + */ + convertTintToImage: false, + + /** + * Whether or not the Canvas BlendModes are supported, consequently the ability to tint using the multiply method. + * + * @memberof PIXI.canvasUtils + * @type {boolean} + */ + canUseMultiply: canUseNewCanvasBlendModes(), + + /** + * The tinting method that will be used. + * + * @memberof PIXI.canvasUtils + * @type {Function} + */ + tintMethod: () => + { // jslint-disable no-empty-function + + }, +}; + +canvasUtils.tintMethod = canvasUtils.canUseMultiply ? canvasUtils.tintWithMultiply : canvasUtils.tintWithPerPixel; diff --git a/packages/canvas/canvas-renderer/src/index.js b/packages/canvas/canvas-renderer/src/index.js index 80987c3..71bc5ee 100644 --- a/packages/canvas/canvas-renderer/src/index.js +++ b/packages/canvas/canvas-renderer/src/index.js @@ -1,6 +1,6 @@ export * from './CanvasRenderer'; export * from './utils/canUseNewCanvasBlendModes'; -export * from './CanvasTinter'; +export * from './canvasUtils'; import './Renderer'; import './BaseTexture'; diff --git a/packages/canvas/canvas-sprite-tiling/src/TilingSprite.js b/packages/canvas/canvas-sprite-tiling/src/TilingSprite.js index 4eac1d7..fd8857f 100644 --- a/packages/canvas/canvas-sprite-tiling/src/TilingSprite.js +++ b/packages/canvas/canvas-sprite-tiling/src/TilingSprite.js @@ -1,5 +1,5 @@ import { TilingSprite } from '@pixi/sprite-tiling'; -import { CanvasTinter } from '@pixi/canvas-renderer'; +import { canvasUtils } from '@pixi/canvas-renderer'; import { CanvasRenderTarget } from '@pixi/utils'; /** @@ -40,7 +40,7 @@ // Tint the tiling sprite if (this.tint !== 0xFFFFFF) { - this._tintedCanvas = CanvasTinter.getTintedCanvas(this, this.tint); + this._tintedCanvas = canvasUtils.getTintedCanvas(this, this.tint); tempCanvas.context.drawImage(this._tintedCanvas, 0, 0); } else diff --git a/packages/canvas/canvas-sprite/src/CanvasSpriteRenderer.js b/packages/canvas/canvas-sprite/src/CanvasSpriteRenderer.js index 507571c..3cdea2a 100644 --- a/packages/canvas/canvas-sprite/src/CanvasSpriteRenderer.js +++ b/packages/canvas/canvas-sprite/src/CanvasSpriteRenderer.js @@ -1,6 +1,6 @@ import { SCALE_MODES, BLEND_MODES } from '@pixi/constants'; -import { Matrix, GroupD8 } from '@pixi/math'; -import { CanvasTinter } from '@pixi/canvas-renderer'; +import { Matrix, groupD8 } from '@pixi/math'; +import { canvasUtils } from '@pixi/canvas-renderer'; const canvasRenderWorldTransform = new Matrix(); @@ -96,7 +96,7 @@ { wt.copyTo(canvasRenderWorldTransform); wt = canvasRenderWorldTransform; - GroupD8.matrixAppendRotationInv(wt, texture.rotate, dx, dy); + groupD8.matrixAppendRotationInv(wt, texture.rotate, dx, dy); // the anchor has already been applied above, so lets set it to zero dx = 0; dy = 0; @@ -155,7 +155,7 @@ sprite._cachedTint = sprite.tint; // TODO clean up caching - how to clean up the caches? - sprite._tintedCanvas = CanvasTinter.getTintedCanvas(sprite, sprite.tint); + sprite._tintedCanvas = canvasUtils.getTintedCanvas(sprite, sprite.tint); } context.drawImage( diff --git a/packages/core/src/textures/Texture.js b/packages/core/src/textures/Texture.js index ea5c843..107b61e 100644 --- a/packages/core/src/textures/Texture.js +++ b/packages/core/src/textures/Texture.js @@ -45,7 +45,7 @@ * @param {PIXI.Rectangle} [frame] - The rectangle frame of the texture to show * @param {PIXI.Rectangle} [orig] - The area of original texture * @param {PIXI.Rectangle} [trim] - Trimmed rectangle of original texture - * @param {number} [rotate] - indicates how the texture was rotated by texture packer. See {@link PIXI.GroupD8} + * @param {number} [rotate] - indicates how the texture was rotated by texture packer. See {@link PIXI.groupD8} * @param {PIXI.Point} [anchor] - Default anchor point used for sprite placement / rotation */ constructor(baseTexture, frame, orig, trim, rotate, anchor) @@ -563,7 +563,7 @@ * set to 2 to compensate for texture packer rotation * set to 6 to compensate for spine packer rotation * can be used to rotate or mirror sprites - * See {@link PIXI.GroupD8} for explanation + * See {@link PIXI.groupD8} for explanation * * @member {number} */ diff --git a/packages/core/src/textures/TextureUvs.js b/packages/core/src/textures/TextureUvs.js index 322e973..735d928 100644 --- a/packages/core/src/textures/TextureUvs.js +++ b/packages/core/src/textures/TextureUvs.js @@ -1,4 +1,4 @@ -import { GroupD8 } from '@pixi/math'; +import { groupD8 } from '@pixi/math'; /** * Stores a texture's frame in UV coordinates, in @@ -85,7 +85,7 @@ * @protected * @param {PIXI.Rectangle} frame - The frame of the texture * @param {PIXI.Rectangle} baseFrame - The base frame of the texture - * @param {number} rotate - Rotation of frame, see {@link PIXI.GroupD8} + * @param {number} rotate - Rotation of frame, see {@link PIXI.groupD8} */ set(frame, baseFrame, rotate) { @@ -102,21 +102,21 @@ const cX = (frame.x / tw) + w2; const cY = (frame.y / th) + h2; - rotate = GroupD8.add(rotate, GroupD8.NW); // NW is top-left corner - this.x0 = cX + (w2 * GroupD8.uX(rotate)); - this.y0 = cY + (h2 * GroupD8.uY(rotate)); + rotate = groupD8.add(rotate, groupD8.NW); // NW is top-left corner + this.x0 = cX + (w2 * groupD8.uX(rotate)); + this.y0 = cY + (h2 * groupD8.uY(rotate)); - rotate = GroupD8.add(rotate, 2); // rotate 90 degrees clockwise - this.x1 = cX + (w2 * GroupD8.uX(rotate)); - this.y1 = cY + (h2 * GroupD8.uY(rotate)); + rotate = groupD8.add(rotate, 2); // rotate 90 degrees clockwise + this.x1 = cX + (w2 * groupD8.uX(rotate)); + this.y1 = cY + (h2 * groupD8.uY(rotate)); - rotate = GroupD8.add(rotate, 2); - this.x2 = cX + (w2 * GroupD8.uX(rotate)); - this.y2 = cY + (h2 * GroupD8.uY(rotate)); + rotate = groupD8.add(rotate, 2); + this.x2 = cX + (w2 * groupD8.uX(rotate)); + this.y2 = cY + (h2 * groupD8.uY(rotate)); - rotate = GroupD8.add(rotate, 2); - this.x3 = cX + (w2 * GroupD8.uX(rotate)); - this.y3 = cY + (h2 * GroupD8.uY(rotate)); + rotate = groupD8.add(rotate, 2); + this.x3 = cX + (w2 * groupD8.uX(rotate)); + this.y3 = cY + (h2 * groupD8.uY(rotate)); } else { diff --git a/bundles/pixi.js-legacy/src/index.js b/bundles/pixi.js-legacy/src/index.js index f1598a1..df6b157 100644 --- a/bundles/pixi.js-legacy/src/index.js +++ b/bundles/pixi.js-legacy/src/index.js @@ -1,5 +1,5 @@ import { accessibility, interaction, prepare, extract } from 'pixi.js'; -import { CanvasRenderer, CanvasTinter } from '@pixi/canvas-renderer'; +import { CanvasRenderer, canvasUtils } from '@pixi/canvas-renderer'; import { CanvasMeshRenderer } from '@pixi/canvas-mesh'; import { CanvasGraphicsRenderer } from '@pixi/canvas-graphics'; import { CanvasSpriteRenderer } from '@pixi/canvas-sprite'; @@ -29,5 +29,5 @@ CanvasGraphicsRenderer, CanvasMeshRenderer, CanvasSpriteRenderer, - CanvasTinter, + canvasUtils, }; diff --git a/bundles/pixi.js/src/useDeprecated.js b/bundles/pixi.js/src/useDeprecated.js index 12f00d4..ab88092 100644 --- a/bundles/pixi.js/src/useDeprecated.js +++ b/bundles/pixi.js/src/useDeprecated.js @@ -138,6 +138,34 @@ return PIXI.systems.FilterSystem; }, }, + + /** + * @namespace PIXI.CanvasTinter + * @see PIXI.canvasUtils + * @deprecated since 5.2.0 + */ + CanvasTinter: { + get() + { + deprecation('5.2.0', 'PIXI.CanvasTinter namespace has moved to PIXI.canvasUtils'); + + return PIXI.canvasUtils; + }, + }, + + /** + * @namespace PIXI.GroupD8 + * @see PIXI.groupD8 + * @deprecated since 5.2.0 + */ + GroupD8: { + get() + { + deprecation('5.2.0', 'PIXI.GroupD8 namespace has moved to PIXI.groupD8'); + + return PIXI.groupD8; + }, + }, }); /** diff --git a/packages/canvas/canvas-mesh/src/NineSlicePlane.js b/packages/canvas/canvas-mesh/src/NineSlicePlane.js index dccfd79..6e2f055 100644 --- a/packages/canvas/canvas-mesh/src/NineSlicePlane.js +++ b/packages/canvas/canvas-mesh/src/NineSlicePlane.js @@ -1,4 +1,4 @@ -import { CanvasTinter } from '@pixi/canvas-renderer'; +import { canvasUtils } from '@pixi/canvas-renderer'; import { NineSlicePlane } from '@pixi/mesh-extras'; /** @@ -50,7 +50,7 @@ this._cachedTint = this.tint; - this._tintedCanvas = CanvasTinter.getTintedCanvas(this, this.tint); + this._tintedCanvas = canvasUtils.getTintedCanvas(this, this.tint); } } diff --git a/packages/canvas/canvas-renderer/src/CanvasTinter.js b/packages/canvas/canvas-renderer/src/CanvasTinter.js deleted file mode 100644 index bfec51a..0000000 --- a/packages/canvas/canvas-renderer/src/CanvasTinter.js +++ /dev/null @@ -1,288 +0,0 @@ -import { hex2rgb, rgb2hex } from '@pixi/utils'; -import { canUseNewCanvasBlendModes } from './utils/canUseNewCanvasBlendModes'; - -/** - * Utility methods for Sprite/Texture tinting. - * - * Tinting with the CanvasRenderer involves creating a new canvas to use as a texture, - * so be aware of the performance implications. - * - * @namespace PIXI.CanvasTinter - * @memberof PIXI - */ -export const CanvasTinter = { - /** - * Basically this method just needs a sprite and a color and tints the sprite with the given color. - * - * @memberof PIXI.CanvasTinter - * @param {PIXI.Sprite} sprite - the sprite to tint - * @param {number} color - the color to use to tint the sprite with - * @return {HTMLCanvasElement} The tinted canvas - */ - getTintedCanvas: (sprite, color) => - { - const texture = sprite.texture; - - color = CanvasTinter.roundColor(color); - - const stringColor = `#${(`00000${(color | 0).toString(16)}`).substr(-6)}`; - - texture.tintCache = texture.tintCache || {}; - - const cachedCanvas = texture.tintCache[stringColor]; - - let canvas; - - if (cachedCanvas) - { - if (cachedCanvas.tintId === texture._updateID) - { - return texture.tintCache[stringColor]; - } - - canvas = texture.tintCache[stringColor]; - } - else - { - canvas = CanvasTinter.canvas || document.createElement('canvas'); - } - - CanvasTinter.tintMethod(texture, color, canvas); - - canvas.tintId = texture._updateID; - - if (CanvasTinter.convertTintToImage) - { - // is this better? - const tintImage = new Image(); - - tintImage.src = canvas.toDataURL(); - - texture.tintCache[stringColor] = tintImage; - } - else - { - texture.tintCache[stringColor] = canvas; - // if we are not converting the texture to an image then we need to lose the reference to the canvas - CanvasTinter.canvas = null; - } - - return canvas; - }, - - /** - * Tint a texture using the 'multiply' operation. - * - * @memberof PIXI.CanvasTinter - * @param {PIXI.Texture} texture - the texture to tint - * @param {number} color - the color to use to tint the sprite with - * @param {HTMLCanvasElement} canvas - the current canvas - */ - tintWithMultiply: (texture, color, canvas) => - { - const context = canvas.getContext('2d'); - const crop = texture._frame.clone(); - const resolution = texture.baseTexture.resolution; - - crop.x *= resolution; - crop.y *= resolution; - crop.width *= resolution; - crop.height *= resolution; - - canvas.width = Math.ceil(crop.width); - canvas.height = Math.ceil(crop.height); - - context.save(); - context.fillStyle = `#${(`00000${(color | 0).toString(16)}`).substr(-6)}`; - - context.fillRect(0, 0, crop.width, crop.height); - - context.globalCompositeOperation = 'multiply'; - - const source = texture.baseTexture.getDrawableSource(); - - context.drawImage( - source, - crop.x, - crop.y, - crop.width, - crop.height, - 0, - 0, - crop.width, - crop.height - ); - - context.globalCompositeOperation = 'destination-atop'; - - context.drawImage( - source, - crop.x, - crop.y, - crop.width, - crop.height, - 0, - 0, - crop.width, - crop.height - ); - context.restore(); - }, - - /** - * Tint a texture using the 'overlay' operation. - * - * @memberof PIXI.CanvasTinter - * @param {PIXI.Texture} texture - the texture to tint - * @param {number} color - the color to use to tint the sprite with - * @param {HTMLCanvasElement} canvas - the current canvas - */ - tintWithOverlay(texture, color, canvas) - { - const context = canvas.getContext('2d'); - const crop = texture._frame.clone(); - const resolution = texture.baseTexture.resolution; - - crop.x *= resolution; - crop.y *= resolution; - crop.width *= resolution; - crop.height *= resolution; - - canvas.width = Math.ceil(crop.width); - canvas.height = Math.ceil(crop.height); - - context.save(); - context.globalCompositeOperation = 'copy'; - context.fillStyle = `#${(`00000${(color | 0).toString(16)}`).substr(-6)}`; - context.fillRect(0, 0, crop.width, crop.height); - - context.globalCompositeOperation = 'destination-atop'; - context.drawImage( - texture.baseTexture.getDrawableSource(), - crop.x, - crop.y, - crop.width, - crop.height, - 0, - 0, - crop.width, - crop.height - ); - - // context.globalCompositeOperation = 'copy'; - context.restore(); - }, - - /** - * Tint a texture pixel per pixel. - * - * @memberof PIXI.CanvasTinter - * @param {PIXI.Texture} texture - the texture to tint - * @param {number} color - the color to use to tint the sprite with - * @param {HTMLCanvasElement} canvas - the current canvas - */ - tintWithPerPixel: (texture, color, canvas) => - { - const context = canvas.getContext('2d'); - const crop = texture._frame.clone(); - const resolution = texture.baseTexture.resolution; - - crop.x *= resolution; - crop.y *= resolution; - crop.width *= resolution; - crop.height *= resolution; - - canvas.width = Math.ceil(crop.width); - canvas.height = Math.ceil(crop.height); - - context.save(); - context.globalCompositeOperation = 'copy'; - context.drawImage( - texture.baseTexture.getDrawableSource(), - crop.x, - crop.y, - crop.width, - crop.height, - 0, - 0, - crop.width, - crop.height - ); - context.restore(); - - const rgbValues = hex2rgb(color); - const r = rgbValues[0]; - const g = rgbValues[1]; - const b = rgbValues[2]; - - const pixelData = context.getImageData(0, 0, crop.width, crop.height); - - const pixels = pixelData.data; - - for (let i = 0; i < pixels.length; i += 4) - { - pixels[i + 0] *= r; - pixels[i + 1] *= g; - pixels[i + 2] *= b; - } - - context.putImageData(pixelData, 0, 0); - }, - - /** - * Rounds the specified color according to the CanvasTinter.cacheStepsPerColorChannel. - * - * @memberof PIXI.CanvasTinter - * @param {number} color - the color to round, should be a hex color - * @return {number} The rounded color. - */ - roundColor: (color) => - { - const step = CanvasTinter.cacheStepsPerColorChannel; - - const rgbValues = hex2rgb(color); - - rgbValues[0] = Math.min(255, (rgbValues[0] / step) * step); - rgbValues[1] = Math.min(255, (rgbValues[1] / step) * step); - rgbValues[2] = Math.min(255, (rgbValues[2] / step) * step); - - return rgb2hex(rgbValues); - }, - - /** - * Number of steps which will be used as a cap when rounding colors. - * - * @memberof PIXI.CanvasTinter - * @type {number} - */ - cacheStepsPerColorChannel: 8, - - /** - * Tint cache boolean flag. - * - * @memberof PIXI.CanvasTinter - * @type {boolean} - */ - convertTintToImage: false, - - /** - * Whether or not the Canvas BlendModes are supported, consequently the ability to tint using the multiply method. - * - * @memberof PIXI.CanvasTinter - * @type {boolean} - */ - canUseMultiply: canUseNewCanvasBlendModes(), - - /** - * The tinting method that will be used. - * - * @memberof PIXI.CanvasTinter - * @type {Function} - */ - tintMethod: () => - { // jslint-disable no-empty-function - - }, -}; - -CanvasTinter.tintMethod = CanvasTinter.canUseMultiply ? CanvasTinter.tintWithMultiply : CanvasTinter.tintWithPerPixel; diff --git a/packages/canvas/canvas-renderer/src/canvasUtils.js b/packages/canvas/canvas-renderer/src/canvasUtils.js new file mode 100644 index 0000000..62e16d4 --- /dev/null +++ b/packages/canvas/canvas-renderer/src/canvasUtils.js @@ -0,0 +1,288 @@ +import { hex2rgb, rgb2hex } from '@pixi/utils'; +import { canUseNewCanvasBlendModes } from './utils/canUseNewCanvasBlendModes'; + +/** + * Utility methods for Sprite/Texture tinting. + * + * Tinting with the CanvasRenderer involves creating a new canvas to use as a texture, + * so be aware of the performance implications. + * + * @namespace PIXI.canvasUtils + * @memberof PIXI + */ +export const canvasUtils = { + /** + * Basically this method just needs a sprite and a color and tints the sprite with the given color. + * + * @memberof PIXI.canvasUtils + * @param {PIXI.Sprite} sprite - the sprite to tint + * @param {number} color - the color to use to tint the sprite with + * @return {HTMLCanvasElement} The tinted canvas + */ + getTintedCanvas: (sprite, color) => + { + const texture = sprite.texture; + + color = canvasUtils.roundColor(color); + + const stringColor = `#${(`00000${(color | 0).toString(16)}`).substr(-6)}`; + + texture.tintCache = texture.tintCache || {}; + + const cachedCanvas = texture.tintCache[stringColor]; + + let canvas; + + if (cachedCanvas) + { + if (cachedCanvas.tintId === texture._updateID) + { + return texture.tintCache[stringColor]; + } + + canvas = texture.tintCache[stringColor]; + } + else + { + canvas = canvasUtils.canvas || document.createElement('canvas'); + } + + canvasUtils.tintMethod(texture, color, canvas); + + canvas.tintId = texture._updateID; + + if (canvasUtils.convertTintToImage) + { + // is this better? + const tintImage = new Image(); + + tintImage.src = canvas.toDataURL(); + + texture.tintCache[stringColor] = tintImage; + } + else + { + texture.tintCache[stringColor] = canvas; + // if we are not converting the texture to an image then we need to lose the reference to the canvas + canvasUtils.canvas = null; + } + + return canvas; + }, + + /** + * Tint a texture using the 'multiply' operation. + * + * @memberof PIXI.canvasUtils + * @param {PIXI.Texture} texture - the texture to tint + * @param {number} color - the color to use to tint the sprite with + * @param {HTMLCanvasElement} canvas - the current canvas + */ + tintWithMultiply: (texture, color, canvas) => + { + const context = canvas.getContext('2d'); + const crop = texture._frame.clone(); + const resolution = texture.baseTexture.resolution; + + crop.x *= resolution; + crop.y *= resolution; + crop.width *= resolution; + crop.height *= resolution; + + canvas.width = Math.ceil(crop.width); + canvas.height = Math.ceil(crop.height); + + context.save(); + context.fillStyle = `#${(`00000${(color | 0).toString(16)}`).substr(-6)}`; + + context.fillRect(0, 0, crop.width, crop.height); + + context.globalCompositeOperation = 'multiply'; + + const source = texture.baseTexture.getDrawableSource(); + + context.drawImage( + source, + crop.x, + crop.y, + crop.width, + crop.height, + 0, + 0, + crop.width, + crop.height + ); + + context.globalCompositeOperation = 'destination-atop'; + + context.drawImage( + source, + crop.x, + crop.y, + crop.width, + crop.height, + 0, + 0, + crop.width, + crop.height + ); + context.restore(); + }, + + /** + * Tint a texture using the 'overlay' operation. + * + * @memberof PIXI.canvasUtils + * @param {PIXI.Texture} texture - the texture to tint + * @param {number} color - the color to use to tint the sprite with + * @param {HTMLCanvasElement} canvas - the current canvas + */ + tintWithOverlay(texture, color, canvas) + { + const context = canvas.getContext('2d'); + const crop = texture._frame.clone(); + const resolution = texture.baseTexture.resolution; + + crop.x *= resolution; + crop.y *= resolution; + crop.width *= resolution; + crop.height *= resolution; + + canvas.width = Math.ceil(crop.width); + canvas.height = Math.ceil(crop.height); + + context.save(); + context.globalCompositeOperation = 'copy'; + context.fillStyle = `#${(`00000${(color | 0).toString(16)}`).substr(-6)}`; + context.fillRect(0, 0, crop.width, crop.height); + + context.globalCompositeOperation = 'destination-atop'; + context.drawImage( + texture.baseTexture.getDrawableSource(), + crop.x, + crop.y, + crop.width, + crop.height, + 0, + 0, + crop.width, + crop.height + ); + + // context.globalCompositeOperation = 'copy'; + context.restore(); + }, + + /** + * Tint a texture pixel per pixel. + * + * @memberof PIXI.canvasUtils + * @param {PIXI.Texture} texture - the texture to tint + * @param {number} color - the color to use to tint the sprite with + * @param {HTMLCanvasElement} canvas - the current canvas + */ + tintWithPerPixel: (texture, color, canvas) => + { + const context = canvas.getContext('2d'); + const crop = texture._frame.clone(); + const resolution = texture.baseTexture.resolution; + + crop.x *= resolution; + crop.y *= resolution; + crop.width *= resolution; + crop.height *= resolution; + + canvas.width = Math.ceil(crop.width); + canvas.height = Math.ceil(crop.height); + + context.save(); + context.globalCompositeOperation = 'copy'; + context.drawImage( + texture.baseTexture.getDrawableSource(), + crop.x, + crop.y, + crop.width, + crop.height, + 0, + 0, + crop.width, + crop.height + ); + context.restore(); + + const rgbValues = hex2rgb(color); + const r = rgbValues[0]; + const g = rgbValues[1]; + const b = rgbValues[2]; + + const pixelData = context.getImageData(0, 0, crop.width, crop.height); + + const pixels = pixelData.data; + + for (let i = 0; i < pixels.length; i += 4) + { + pixels[i + 0] *= r; + pixels[i + 1] *= g; + pixels[i + 2] *= b; + } + + context.putImageData(pixelData, 0, 0); + }, + + /** + * Rounds the specified color according to the canvasUtils.cacheStepsPerColorChannel. + * + * @memberof PIXI.canvasUtils + * @param {number} color - the color to round, should be a hex color + * @return {number} The rounded color. + */ + roundColor: (color) => + { + const step = canvasUtils.cacheStepsPerColorChannel; + + const rgbValues = hex2rgb(color); + + rgbValues[0] = Math.min(255, (rgbValues[0] / step) * step); + rgbValues[1] = Math.min(255, (rgbValues[1] / step) * step); + rgbValues[2] = Math.min(255, (rgbValues[2] / step) * step); + + return rgb2hex(rgbValues); + }, + + /** + * Number of steps which will be used as a cap when rounding colors. + * + * @memberof PIXI.canvasUtils + * @type {number} + */ + cacheStepsPerColorChannel: 8, + + /** + * Tint cache boolean flag. + * + * @memberof PIXI.canvasUtils + * @type {boolean} + */ + convertTintToImage: false, + + /** + * Whether or not the Canvas BlendModes are supported, consequently the ability to tint using the multiply method. + * + * @memberof PIXI.canvasUtils + * @type {boolean} + */ + canUseMultiply: canUseNewCanvasBlendModes(), + + /** + * The tinting method that will be used. + * + * @memberof PIXI.canvasUtils + * @type {Function} + */ + tintMethod: () => + { // jslint-disable no-empty-function + + }, +}; + +canvasUtils.tintMethod = canvasUtils.canUseMultiply ? canvasUtils.tintWithMultiply : canvasUtils.tintWithPerPixel; diff --git a/packages/canvas/canvas-renderer/src/index.js b/packages/canvas/canvas-renderer/src/index.js index 80987c3..71bc5ee 100644 --- a/packages/canvas/canvas-renderer/src/index.js +++ b/packages/canvas/canvas-renderer/src/index.js @@ -1,6 +1,6 @@ export * from './CanvasRenderer'; export * from './utils/canUseNewCanvasBlendModes'; -export * from './CanvasTinter'; +export * from './canvasUtils'; import './Renderer'; import './BaseTexture'; diff --git a/packages/canvas/canvas-sprite-tiling/src/TilingSprite.js b/packages/canvas/canvas-sprite-tiling/src/TilingSprite.js index 4eac1d7..fd8857f 100644 --- a/packages/canvas/canvas-sprite-tiling/src/TilingSprite.js +++ b/packages/canvas/canvas-sprite-tiling/src/TilingSprite.js @@ -1,5 +1,5 @@ import { TilingSprite } from '@pixi/sprite-tiling'; -import { CanvasTinter } from '@pixi/canvas-renderer'; +import { canvasUtils } from '@pixi/canvas-renderer'; import { CanvasRenderTarget } from '@pixi/utils'; /** @@ -40,7 +40,7 @@ // Tint the tiling sprite if (this.tint !== 0xFFFFFF) { - this._tintedCanvas = CanvasTinter.getTintedCanvas(this, this.tint); + this._tintedCanvas = canvasUtils.getTintedCanvas(this, this.tint); tempCanvas.context.drawImage(this._tintedCanvas, 0, 0); } else diff --git a/packages/canvas/canvas-sprite/src/CanvasSpriteRenderer.js b/packages/canvas/canvas-sprite/src/CanvasSpriteRenderer.js index 507571c..3cdea2a 100644 --- a/packages/canvas/canvas-sprite/src/CanvasSpriteRenderer.js +++ b/packages/canvas/canvas-sprite/src/CanvasSpriteRenderer.js @@ -1,6 +1,6 @@ import { SCALE_MODES, BLEND_MODES } from '@pixi/constants'; -import { Matrix, GroupD8 } from '@pixi/math'; -import { CanvasTinter } from '@pixi/canvas-renderer'; +import { Matrix, groupD8 } from '@pixi/math'; +import { canvasUtils } from '@pixi/canvas-renderer'; const canvasRenderWorldTransform = new Matrix(); @@ -96,7 +96,7 @@ { wt.copyTo(canvasRenderWorldTransform); wt = canvasRenderWorldTransform; - GroupD8.matrixAppendRotationInv(wt, texture.rotate, dx, dy); + groupD8.matrixAppendRotationInv(wt, texture.rotate, dx, dy); // the anchor has already been applied above, so lets set it to zero dx = 0; dy = 0; @@ -155,7 +155,7 @@ sprite._cachedTint = sprite.tint; // TODO clean up caching - how to clean up the caches? - sprite._tintedCanvas = CanvasTinter.getTintedCanvas(sprite, sprite.tint); + sprite._tintedCanvas = canvasUtils.getTintedCanvas(sprite, sprite.tint); } context.drawImage( diff --git a/packages/core/src/textures/Texture.js b/packages/core/src/textures/Texture.js index ea5c843..107b61e 100644 --- a/packages/core/src/textures/Texture.js +++ b/packages/core/src/textures/Texture.js @@ -45,7 +45,7 @@ * @param {PIXI.Rectangle} [frame] - The rectangle frame of the texture to show * @param {PIXI.Rectangle} [orig] - The area of original texture * @param {PIXI.Rectangle} [trim] - Trimmed rectangle of original texture - * @param {number} [rotate] - indicates how the texture was rotated by texture packer. See {@link PIXI.GroupD8} + * @param {number} [rotate] - indicates how the texture was rotated by texture packer. See {@link PIXI.groupD8} * @param {PIXI.Point} [anchor] - Default anchor point used for sprite placement / rotation */ constructor(baseTexture, frame, orig, trim, rotate, anchor) @@ -563,7 +563,7 @@ * set to 2 to compensate for texture packer rotation * set to 6 to compensate for spine packer rotation * can be used to rotate or mirror sprites - * See {@link PIXI.GroupD8} for explanation + * See {@link PIXI.groupD8} for explanation * * @member {number} */ diff --git a/packages/core/src/textures/TextureUvs.js b/packages/core/src/textures/TextureUvs.js index 322e973..735d928 100644 --- a/packages/core/src/textures/TextureUvs.js +++ b/packages/core/src/textures/TextureUvs.js @@ -1,4 +1,4 @@ -import { GroupD8 } from '@pixi/math'; +import { groupD8 } from '@pixi/math'; /** * Stores a texture's frame in UV coordinates, in @@ -85,7 +85,7 @@ * @protected * @param {PIXI.Rectangle} frame - The frame of the texture * @param {PIXI.Rectangle} baseFrame - The base frame of the texture - * @param {number} rotate - Rotation of frame, see {@link PIXI.GroupD8} + * @param {number} rotate - Rotation of frame, see {@link PIXI.groupD8} */ set(frame, baseFrame, rotate) { @@ -102,21 +102,21 @@ const cX = (frame.x / tw) + w2; const cY = (frame.y / th) + h2; - rotate = GroupD8.add(rotate, GroupD8.NW); // NW is top-left corner - this.x0 = cX + (w2 * GroupD8.uX(rotate)); - this.y0 = cY + (h2 * GroupD8.uY(rotate)); + rotate = groupD8.add(rotate, groupD8.NW); // NW is top-left corner + this.x0 = cX + (w2 * groupD8.uX(rotate)); + this.y0 = cY + (h2 * groupD8.uY(rotate)); - rotate = GroupD8.add(rotate, 2); // rotate 90 degrees clockwise - this.x1 = cX + (w2 * GroupD8.uX(rotate)); - this.y1 = cY + (h2 * GroupD8.uY(rotate)); + rotate = groupD8.add(rotate, 2); // rotate 90 degrees clockwise + this.x1 = cX + (w2 * groupD8.uX(rotate)); + this.y1 = cY + (h2 * groupD8.uY(rotate)); - rotate = GroupD8.add(rotate, 2); - this.x2 = cX + (w2 * GroupD8.uX(rotate)); - this.y2 = cY + (h2 * GroupD8.uY(rotate)); + rotate = groupD8.add(rotate, 2); + this.x2 = cX + (w2 * groupD8.uX(rotate)); + this.y2 = cY + (h2 * groupD8.uY(rotate)); - rotate = GroupD8.add(rotate, 2); - this.x3 = cX + (w2 * GroupD8.uX(rotate)); - this.y3 = cY + (h2 * GroupD8.uY(rotate)); + rotate = groupD8.add(rotate, 2); + this.x3 = cX + (w2 * groupD8.uX(rotate)); + this.y3 = cY + (h2 * groupD8.uY(rotate)); } else { diff --git a/packages/math/src/GroupD8.js b/packages/math/src/GroupD8.js deleted file mode 100644 index 513cea8..0000000 --- a/packages/math/src/GroupD8.js +++ /dev/null @@ -1,403 +0,0 @@ -// Your friendly neighbour https://en.wikipedia.org/wiki/Dihedral_group -// -// This file implements the dihedral group of order 16, also called -// of degree 8. That's why its called GroupD8. - -import { Matrix } from './Matrix'; - -/* - * Transform matrix for operation n is: - * | ux | vx | - * | uy | vy | - */ - -const ux = [1, 1, 0, -1, -1, -1, 0, 1, 1, 1, 0, -1, -1, -1, 0, 1]; -const uy = [0, 1, 1, 1, 0, -1, -1, -1, 0, 1, 1, 1, 0, -1, -1, -1]; -const vx = [0, -1, -1, -1, 0, 1, 1, 1, 0, 1, 1, 1, 0, -1, -1, -1]; -const vy = [1, 1, 0, -1, -1, -1, 0, 1, -1, -1, 0, 1, 1, 1, 0, -1]; - -/** - * [Cayley Table]{@link https://en.wikipedia.org/wiki/Cayley_table} - * for the composition of each rotation in the dihederal group D8. - * - * @type number[][] - * @private - */ -const rotationCayley = []; - -/** - * Matrices for each `GD8Symmetry` rotation. - * - * @type Matrix[] - * @private - */ -const rotationMatrices = []; - -/* - * Alias for {@code Math.sign}. - */ -const signum = Math.sign; - -/* - * Initializes `rotationCayley` and `rotationMatrices`. It is called - * only once below. - */ -function init() -{ - for (let i = 0; i < 16; i++) - { - const row = []; - - rotationCayley.push(row); - - for (let j = 0; j < 16; j++) - { - /* Multiplies rotation matrices i and j. */ - const _ux = signum((ux[i] * ux[j]) + (vx[i] * uy[j])); - const _uy = signum((uy[i] * ux[j]) + (vy[i] * uy[j])); - const _vx = signum((ux[i] * vx[j]) + (vx[i] * vy[j])); - const _vy = signum((uy[i] * vx[j]) + (vy[i] * vy[j])); - - /* Finds rotation matrix matching the product and pushes it. */ - for (let k = 0; k < 16; k++) - { - if (ux[k] === _ux && uy[k] === _uy - && vx[k] === _vx && vy[k] === _vy) - { - row.push(k); - break; - } - } - } - } - - for (let i = 0; i < 16; i++) - { - const mat = new Matrix(); - - mat.set(ux[i], uy[i], vx[i], vy[i], 0, 0); - rotationMatrices.push(mat); - } -} - -init(); - -/** - * @memberof PIXI - * @typedef {number} GD8Symmetry - * @see PIXI.GroupD8 - */ - -/** - * Implements the dihedral group D8, which is similar to - * [group D4]{@link http://mathworld.wolfram.com/DihedralGroupD4.html}; - * D8 is the same but with diagonals, and it is used for texture - * rotations. - * - * The directions the U- and V- axes after rotation - * of an angle of `a: GD8Constant` are the vectors `(uX(a), uY(a))` - * and `(vX(a), vY(a))`. These aren't necessarily unit vectors. - * - * **Origin:**
- * This is the small part of gameofbombs.com portal system. It works. - * - * @see PIXI.GroupD8.E - * @see PIXI.GroupD8.SE - * @see PIXI.GroupD8.S - * @see PIXI.GroupD8.SW - * @see PIXI.GroupD8.W - * @see PIXI.GroupD8.NW - * @see PIXI.GroupD8.N - * @see PIXI.GroupD8.NE - * @author Ivan @ivanpopelyshev - * @namespace PIXI.GroupD8 - * @memberof PIXI - */ -export const GroupD8 = { - /** - * | Rotation | Direction | - * |----------|-----------| - * | 0° | East | - * - * @memberof PIXI.GroupD8 - * @constant {PIXI.GD8Symmetry} - */ - E: 0, - - /** - * | Rotation | Direction | - * |----------|-----------| - * | 45°↻ | Southeast | - * - * @memberof PIXI.GroupD8 - * @constant {PIXI.GD8Symmetry} - */ - SE: 1, - - /** - * | Rotation | Direction | - * |----------|-----------| - * | 90°↻ | South | - * - * @memberof PIXI.GroupD8 - * @constant {PIXI.GD8Symmetry} - */ - S: 2, - - /** - * | Rotation | Direction | - * |----------|-----------| - * | 135°↻ | Southwest | - * - * @memberof PIXI.GroupD8 - * @constant {PIXI.GD8Symmetry} - */ - SW: 3, - - /** - * | Rotation | Direction | - * |----------|-----------| - * | 180° | West | - * - * @memberof PIXI.GroupD8 - * @constant {PIXI.GD8Symmetry} - */ - W: 4, - - /** - * | Rotation | Direction | - * |-------------|--------------| - * | -135°/225°↻ | Northwest | - * - * @memberof PIXI.GroupD8 - * @constant {PIXI.GD8Symmetry} - */ - NW: 5, - - /** - * | Rotation | Direction | - * |-------------|--------------| - * | -90°/270°↻ | North | - * - * @memberof PIXI.GroupD8 - * @constant {PIXI.GD8Symmetry} - */ - N: 6, - - /** - * | Rotation | Direction | - * |-------------|--------------| - * | -45°/315°↻ | Northeast | - * - * @memberof PIXI.GroupD8 - * @constant {PIXI.GD8Symmetry} - */ - NE: 7, - - /** - * Reflection about Y-axis. - * - * @memberof PIXI.GroupD8 - * @constant {PIXI.GD8Symmetry} - */ - MIRROR_VERTICAL: 8, - - /** - * Reflection about the main diagonal. - * - * @memberof PIXI.GroupD8 - * @constant {PIXI.GD8Symmetry} - */ - MAIN_DIAGONAL: 10, - - /** - * Reflection about X-axis. - * - * @memberof PIXI.GroupD8 - * @constant {PIXI.GD8Symmetry} - */ - MIRROR_HORIZONTAL: 12, - - /** - * Reflection about reverse diagonal. - * - * @memberof PIXI.GroupD8 - * @constant {PIXI.GD8Symmetry} - */ - REVERSE_DIAGONAL: 14, - - /** - * @memberof PIXI.GroupD8 - * @param {PIXI.GD8Symmetry} ind - sprite rotation angle. - * @return {PIXI.GD8Symmetry} The X-component of the U-axis - * after rotating the axes. - */ - uX: (ind) => ux[ind], - - /** - * @memberof PIXI.GroupD8 - * @param {PIXI.GD8Symmetry} ind - sprite rotation angle. - * @return {PIXI.GD8Symmetry} The Y-component of the U-axis - * after rotating the axes. - */ - uY: (ind) => uy[ind], - - /** - * @memberof PIXI.GroupD8 - * @param {PIXI.GD8Symmetry} ind - sprite rotation angle. - * @return {PIXI.GD8Symmetry} The X-component of the V-axis - * after rotating the axes. - */ - vX: (ind) => vx[ind], - - /** - * @memberof PIXI.GroupD8 - * @param {PIXI.GD8Symmetry} ind - sprite rotation angle. - * @return {PIXI.GD8Symmetry} The Y-component of the V-axis - * after rotating the axes. - */ - vY: (ind) => vy[ind], - - /** - * @memberof PIXI.GroupD8 - * @param {PIXI.GD8Symmetry} rotation - symmetry whose opposite - * is needed. Only rotations have opposite symmetries while - * reflections don't. - * @return {PIXI.GD8Symmetry} The opposite symmetry of `rotation` - */ - inv: (rotation) => - { - if (rotation & 8)// true only if between 8 & 15 (reflections) - { - return rotation & 15;// or rotation % 16 - } - - return (-rotation) & 7;// or (8 - rotation) % 8 - }, - - /** - * Composes the two D8 operations. - * - * Taking `^` as reflection: - * - * | | E=0 | S=2 | W=4 | N=6 | E^=8 | S^=10 | W^=12 | N^=14 | - * |-------|-----|-----|-----|-----|------|-------|-------|-------| - * | E=0 | E | S | W | N | E^ | S^ | W^ | N^ | - * | S=2 | S | W | N | E | S^ | W^ | N^ | E^ | - * | W=4 | W | N | E | S | W^ | N^ | E^ | S^ | - * | N=6 | N | E | S | W | N^ | E^ | S^ | W^ | - * | E^=8 | E^ | N^ | W^ | S^ | E | N | W | S | - * | S^=10 | S^ | E^ | N^ | W^ | S | E | N | W | - * | W^=12 | W^ | S^ | E^ | N^ | W | S | E | N | - * | N^=14 | N^ | W^ | S^ | E^ | N | W | S | E | - * - * [This is a Cayley table]{@link https://en.wikipedia.org/wiki/Cayley_table} - * @memberof PIXI.GroupD8 - * @param {PIXI.GD8Symmetry} rotationSecond - Second operation, which - * is the row in the above cayley table. - * @param {PIXI.GD8Symmetry} rotationFirst - First operation, which - * is the column in the above cayley table. - * @return {PIXI.GD8Symmetry} Composed operation - */ - add: (rotationSecond, rotationFirst) => ( - rotationCayley[rotationSecond][rotationFirst] - ), - - /** - * Reverse of `add`. - * - * @memberof PIXI.GroupD8 - * @param {PIXI.GD8Symmetry} rotationSecond - Second operation - * @param {PIXI.GD8Symmetry} rotationFirst - First operation - * @return {PIXI.GD8Symmetry} Result - */ - sub: (rotationSecond, rotationFirst) => ( - rotationCayley[rotationSecond][GroupD8.inv(rotationFirst)] - ), - - /** - * Adds 180 degrees to rotation, which is a commutative - * operation. - * - * @memberof PIXI.GroupD8 - * @param {number} rotation - The number to rotate. - * @returns {number} Rotated number - */ - rotate180: (rotation) => rotation ^ 4, - - /** - * Checks if the rotation angle is vertical, i.e. south - * or north. It doesn't work for reflections. - * - * @memberof PIXI.GroupD8 - * @param {PIXI.GD8Symmetry} rotation - The number to check. - * @returns {boolean} Whether or not the direction is vertical - */ - isVertical: (rotation) => (rotation & 3) === 2, // rotation % 4 === 2 - - /** - * Approximates the vector `V(dx,dy)` into one of the - * eight directions provided by `GroupD8`. - * - * @memberof PIXI.GroupD8 - * @param {number} dx - X-component of the vector - * @param {number} dy - Y-component of the vector - * @return {PIXI.GD8Symmetry} Approximation of the vector into - * one of the eight symmetries. - */ - byDirection: (dx, dy) => - { - if (Math.abs(dx) * 2 <= Math.abs(dy)) - { - if (dy >= 0) - { - return GroupD8.S; - } - - return GroupD8.N; - } - else if (Math.abs(dy) * 2 <= Math.abs(dx)) - { - if (dx > 0) - { - return GroupD8.E; - } - - return GroupD8.W; - } - else if (dy > 0) - { - if (dx > 0) - { - return GroupD8.SE; - } - - return GroupD8.SW; - } - else if (dx > 0) - { - return GroupD8.NE; - } - - return GroupD8.NW; - }, - - /** - * Helps sprite to compensate texture packer rotation. - * - * @memberof PIXI.GroupD8 - * @param {PIXI.Matrix} matrix - sprite world matrix - * @param {PIXI.GD8Symmetry} rotation - The rotation factor to use. - * @param {number} tx - sprite anchoring - * @param {number} ty - sprite anchoring - */ - matrixAppendRotationInv: (matrix, rotation, tx = 0, ty = 0) => - { - // Packer used "rotation", we use "inv(rotation)" - const mat = rotationMatrices[GroupD8.inv(rotation)]; - - mat.tx = tx; - mat.ty = ty; - matrix.append(mat); - }, -}; diff --git a/bundles/pixi.js-legacy/src/index.js b/bundles/pixi.js-legacy/src/index.js index f1598a1..df6b157 100644 --- a/bundles/pixi.js-legacy/src/index.js +++ b/bundles/pixi.js-legacy/src/index.js @@ -1,5 +1,5 @@ import { accessibility, interaction, prepare, extract } from 'pixi.js'; -import { CanvasRenderer, CanvasTinter } from '@pixi/canvas-renderer'; +import { CanvasRenderer, canvasUtils } from '@pixi/canvas-renderer'; import { CanvasMeshRenderer } from '@pixi/canvas-mesh'; import { CanvasGraphicsRenderer } from '@pixi/canvas-graphics'; import { CanvasSpriteRenderer } from '@pixi/canvas-sprite'; @@ -29,5 +29,5 @@ CanvasGraphicsRenderer, CanvasMeshRenderer, CanvasSpriteRenderer, - CanvasTinter, + canvasUtils, }; diff --git a/bundles/pixi.js/src/useDeprecated.js b/bundles/pixi.js/src/useDeprecated.js index 12f00d4..ab88092 100644 --- a/bundles/pixi.js/src/useDeprecated.js +++ b/bundles/pixi.js/src/useDeprecated.js @@ -138,6 +138,34 @@ return PIXI.systems.FilterSystem; }, }, + + /** + * @namespace PIXI.CanvasTinter + * @see PIXI.canvasUtils + * @deprecated since 5.2.0 + */ + CanvasTinter: { + get() + { + deprecation('5.2.0', 'PIXI.CanvasTinter namespace has moved to PIXI.canvasUtils'); + + return PIXI.canvasUtils; + }, + }, + + /** + * @namespace PIXI.GroupD8 + * @see PIXI.groupD8 + * @deprecated since 5.2.0 + */ + GroupD8: { + get() + { + deprecation('5.2.0', 'PIXI.GroupD8 namespace has moved to PIXI.groupD8'); + + return PIXI.groupD8; + }, + }, }); /** diff --git a/packages/canvas/canvas-mesh/src/NineSlicePlane.js b/packages/canvas/canvas-mesh/src/NineSlicePlane.js index dccfd79..6e2f055 100644 --- a/packages/canvas/canvas-mesh/src/NineSlicePlane.js +++ b/packages/canvas/canvas-mesh/src/NineSlicePlane.js @@ -1,4 +1,4 @@ -import { CanvasTinter } from '@pixi/canvas-renderer'; +import { canvasUtils } from '@pixi/canvas-renderer'; import { NineSlicePlane } from '@pixi/mesh-extras'; /** @@ -50,7 +50,7 @@ this._cachedTint = this.tint; - this._tintedCanvas = CanvasTinter.getTintedCanvas(this, this.tint); + this._tintedCanvas = canvasUtils.getTintedCanvas(this, this.tint); } } diff --git a/packages/canvas/canvas-renderer/src/CanvasTinter.js b/packages/canvas/canvas-renderer/src/CanvasTinter.js deleted file mode 100644 index bfec51a..0000000 --- a/packages/canvas/canvas-renderer/src/CanvasTinter.js +++ /dev/null @@ -1,288 +0,0 @@ -import { hex2rgb, rgb2hex } from '@pixi/utils'; -import { canUseNewCanvasBlendModes } from './utils/canUseNewCanvasBlendModes'; - -/** - * Utility methods for Sprite/Texture tinting. - * - * Tinting with the CanvasRenderer involves creating a new canvas to use as a texture, - * so be aware of the performance implications. - * - * @namespace PIXI.CanvasTinter - * @memberof PIXI - */ -export const CanvasTinter = { - /** - * Basically this method just needs a sprite and a color and tints the sprite with the given color. - * - * @memberof PIXI.CanvasTinter - * @param {PIXI.Sprite} sprite - the sprite to tint - * @param {number} color - the color to use to tint the sprite with - * @return {HTMLCanvasElement} The tinted canvas - */ - getTintedCanvas: (sprite, color) => - { - const texture = sprite.texture; - - color = CanvasTinter.roundColor(color); - - const stringColor = `#${(`00000${(color | 0).toString(16)}`).substr(-6)}`; - - texture.tintCache = texture.tintCache || {}; - - const cachedCanvas = texture.tintCache[stringColor]; - - let canvas; - - if (cachedCanvas) - { - if (cachedCanvas.tintId === texture._updateID) - { - return texture.tintCache[stringColor]; - } - - canvas = texture.tintCache[stringColor]; - } - else - { - canvas = CanvasTinter.canvas || document.createElement('canvas'); - } - - CanvasTinter.tintMethod(texture, color, canvas); - - canvas.tintId = texture._updateID; - - if (CanvasTinter.convertTintToImage) - { - // is this better? - const tintImage = new Image(); - - tintImage.src = canvas.toDataURL(); - - texture.tintCache[stringColor] = tintImage; - } - else - { - texture.tintCache[stringColor] = canvas; - // if we are not converting the texture to an image then we need to lose the reference to the canvas - CanvasTinter.canvas = null; - } - - return canvas; - }, - - /** - * Tint a texture using the 'multiply' operation. - * - * @memberof PIXI.CanvasTinter - * @param {PIXI.Texture} texture - the texture to tint - * @param {number} color - the color to use to tint the sprite with - * @param {HTMLCanvasElement} canvas - the current canvas - */ - tintWithMultiply: (texture, color, canvas) => - { - const context = canvas.getContext('2d'); - const crop = texture._frame.clone(); - const resolution = texture.baseTexture.resolution; - - crop.x *= resolution; - crop.y *= resolution; - crop.width *= resolution; - crop.height *= resolution; - - canvas.width = Math.ceil(crop.width); - canvas.height = Math.ceil(crop.height); - - context.save(); - context.fillStyle = `#${(`00000${(color | 0).toString(16)}`).substr(-6)}`; - - context.fillRect(0, 0, crop.width, crop.height); - - context.globalCompositeOperation = 'multiply'; - - const source = texture.baseTexture.getDrawableSource(); - - context.drawImage( - source, - crop.x, - crop.y, - crop.width, - crop.height, - 0, - 0, - crop.width, - crop.height - ); - - context.globalCompositeOperation = 'destination-atop'; - - context.drawImage( - source, - crop.x, - crop.y, - crop.width, - crop.height, - 0, - 0, - crop.width, - crop.height - ); - context.restore(); - }, - - /** - * Tint a texture using the 'overlay' operation. - * - * @memberof PIXI.CanvasTinter - * @param {PIXI.Texture} texture - the texture to tint - * @param {number} color - the color to use to tint the sprite with - * @param {HTMLCanvasElement} canvas - the current canvas - */ - tintWithOverlay(texture, color, canvas) - { - const context = canvas.getContext('2d'); - const crop = texture._frame.clone(); - const resolution = texture.baseTexture.resolution; - - crop.x *= resolution; - crop.y *= resolution; - crop.width *= resolution; - crop.height *= resolution; - - canvas.width = Math.ceil(crop.width); - canvas.height = Math.ceil(crop.height); - - context.save(); - context.globalCompositeOperation = 'copy'; - context.fillStyle = `#${(`00000${(color | 0).toString(16)}`).substr(-6)}`; - context.fillRect(0, 0, crop.width, crop.height); - - context.globalCompositeOperation = 'destination-atop'; - context.drawImage( - texture.baseTexture.getDrawableSource(), - crop.x, - crop.y, - crop.width, - crop.height, - 0, - 0, - crop.width, - crop.height - ); - - // context.globalCompositeOperation = 'copy'; - context.restore(); - }, - - /** - * Tint a texture pixel per pixel. - * - * @memberof PIXI.CanvasTinter - * @param {PIXI.Texture} texture - the texture to tint - * @param {number} color - the color to use to tint the sprite with - * @param {HTMLCanvasElement} canvas - the current canvas - */ - tintWithPerPixel: (texture, color, canvas) => - { - const context = canvas.getContext('2d'); - const crop = texture._frame.clone(); - const resolution = texture.baseTexture.resolution; - - crop.x *= resolution; - crop.y *= resolution; - crop.width *= resolution; - crop.height *= resolution; - - canvas.width = Math.ceil(crop.width); - canvas.height = Math.ceil(crop.height); - - context.save(); - context.globalCompositeOperation = 'copy'; - context.drawImage( - texture.baseTexture.getDrawableSource(), - crop.x, - crop.y, - crop.width, - crop.height, - 0, - 0, - crop.width, - crop.height - ); - context.restore(); - - const rgbValues = hex2rgb(color); - const r = rgbValues[0]; - const g = rgbValues[1]; - const b = rgbValues[2]; - - const pixelData = context.getImageData(0, 0, crop.width, crop.height); - - const pixels = pixelData.data; - - for (let i = 0; i < pixels.length; i += 4) - { - pixels[i + 0] *= r; - pixels[i + 1] *= g; - pixels[i + 2] *= b; - } - - context.putImageData(pixelData, 0, 0); - }, - - /** - * Rounds the specified color according to the CanvasTinter.cacheStepsPerColorChannel. - * - * @memberof PIXI.CanvasTinter - * @param {number} color - the color to round, should be a hex color - * @return {number} The rounded color. - */ - roundColor: (color) => - { - const step = CanvasTinter.cacheStepsPerColorChannel; - - const rgbValues = hex2rgb(color); - - rgbValues[0] = Math.min(255, (rgbValues[0] / step) * step); - rgbValues[1] = Math.min(255, (rgbValues[1] / step) * step); - rgbValues[2] = Math.min(255, (rgbValues[2] / step) * step); - - return rgb2hex(rgbValues); - }, - - /** - * Number of steps which will be used as a cap when rounding colors. - * - * @memberof PIXI.CanvasTinter - * @type {number} - */ - cacheStepsPerColorChannel: 8, - - /** - * Tint cache boolean flag. - * - * @memberof PIXI.CanvasTinter - * @type {boolean} - */ - convertTintToImage: false, - - /** - * Whether or not the Canvas BlendModes are supported, consequently the ability to tint using the multiply method. - * - * @memberof PIXI.CanvasTinter - * @type {boolean} - */ - canUseMultiply: canUseNewCanvasBlendModes(), - - /** - * The tinting method that will be used. - * - * @memberof PIXI.CanvasTinter - * @type {Function} - */ - tintMethod: () => - { // jslint-disable no-empty-function - - }, -}; - -CanvasTinter.tintMethod = CanvasTinter.canUseMultiply ? CanvasTinter.tintWithMultiply : CanvasTinter.tintWithPerPixel; diff --git a/packages/canvas/canvas-renderer/src/canvasUtils.js b/packages/canvas/canvas-renderer/src/canvasUtils.js new file mode 100644 index 0000000..62e16d4 --- /dev/null +++ b/packages/canvas/canvas-renderer/src/canvasUtils.js @@ -0,0 +1,288 @@ +import { hex2rgb, rgb2hex } from '@pixi/utils'; +import { canUseNewCanvasBlendModes } from './utils/canUseNewCanvasBlendModes'; + +/** + * Utility methods for Sprite/Texture tinting. + * + * Tinting with the CanvasRenderer involves creating a new canvas to use as a texture, + * so be aware of the performance implications. + * + * @namespace PIXI.canvasUtils + * @memberof PIXI + */ +export const canvasUtils = { + /** + * Basically this method just needs a sprite and a color and tints the sprite with the given color. + * + * @memberof PIXI.canvasUtils + * @param {PIXI.Sprite} sprite - the sprite to tint + * @param {number} color - the color to use to tint the sprite with + * @return {HTMLCanvasElement} The tinted canvas + */ + getTintedCanvas: (sprite, color) => + { + const texture = sprite.texture; + + color = canvasUtils.roundColor(color); + + const stringColor = `#${(`00000${(color | 0).toString(16)}`).substr(-6)}`; + + texture.tintCache = texture.tintCache || {}; + + const cachedCanvas = texture.tintCache[stringColor]; + + let canvas; + + if (cachedCanvas) + { + if (cachedCanvas.tintId === texture._updateID) + { + return texture.tintCache[stringColor]; + } + + canvas = texture.tintCache[stringColor]; + } + else + { + canvas = canvasUtils.canvas || document.createElement('canvas'); + } + + canvasUtils.tintMethod(texture, color, canvas); + + canvas.tintId = texture._updateID; + + if (canvasUtils.convertTintToImage) + { + // is this better? + const tintImage = new Image(); + + tintImage.src = canvas.toDataURL(); + + texture.tintCache[stringColor] = tintImage; + } + else + { + texture.tintCache[stringColor] = canvas; + // if we are not converting the texture to an image then we need to lose the reference to the canvas + canvasUtils.canvas = null; + } + + return canvas; + }, + + /** + * Tint a texture using the 'multiply' operation. + * + * @memberof PIXI.canvasUtils + * @param {PIXI.Texture} texture - the texture to tint + * @param {number} color - the color to use to tint the sprite with + * @param {HTMLCanvasElement} canvas - the current canvas + */ + tintWithMultiply: (texture, color, canvas) => + { + const context = canvas.getContext('2d'); + const crop = texture._frame.clone(); + const resolution = texture.baseTexture.resolution; + + crop.x *= resolution; + crop.y *= resolution; + crop.width *= resolution; + crop.height *= resolution; + + canvas.width = Math.ceil(crop.width); + canvas.height = Math.ceil(crop.height); + + context.save(); + context.fillStyle = `#${(`00000${(color | 0).toString(16)}`).substr(-6)}`; + + context.fillRect(0, 0, crop.width, crop.height); + + context.globalCompositeOperation = 'multiply'; + + const source = texture.baseTexture.getDrawableSource(); + + context.drawImage( + source, + crop.x, + crop.y, + crop.width, + crop.height, + 0, + 0, + crop.width, + crop.height + ); + + context.globalCompositeOperation = 'destination-atop'; + + context.drawImage( + source, + crop.x, + crop.y, + crop.width, + crop.height, + 0, + 0, + crop.width, + crop.height + ); + context.restore(); + }, + + /** + * Tint a texture using the 'overlay' operation. + * + * @memberof PIXI.canvasUtils + * @param {PIXI.Texture} texture - the texture to tint + * @param {number} color - the color to use to tint the sprite with + * @param {HTMLCanvasElement} canvas - the current canvas + */ + tintWithOverlay(texture, color, canvas) + { + const context = canvas.getContext('2d'); + const crop = texture._frame.clone(); + const resolution = texture.baseTexture.resolution; + + crop.x *= resolution; + crop.y *= resolution; + crop.width *= resolution; + crop.height *= resolution; + + canvas.width = Math.ceil(crop.width); + canvas.height = Math.ceil(crop.height); + + context.save(); + context.globalCompositeOperation = 'copy'; + context.fillStyle = `#${(`00000${(color | 0).toString(16)}`).substr(-6)}`; + context.fillRect(0, 0, crop.width, crop.height); + + context.globalCompositeOperation = 'destination-atop'; + context.drawImage( + texture.baseTexture.getDrawableSource(), + crop.x, + crop.y, + crop.width, + crop.height, + 0, + 0, + crop.width, + crop.height + ); + + // context.globalCompositeOperation = 'copy'; + context.restore(); + }, + + /** + * Tint a texture pixel per pixel. + * + * @memberof PIXI.canvasUtils + * @param {PIXI.Texture} texture - the texture to tint + * @param {number} color - the color to use to tint the sprite with + * @param {HTMLCanvasElement} canvas - the current canvas + */ + tintWithPerPixel: (texture, color, canvas) => + { + const context = canvas.getContext('2d'); + const crop = texture._frame.clone(); + const resolution = texture.baseTexture.resolution; + + crop.x *= resolution; + crop.y *= resolution; + crop.width *= resolution; + crop.height *= resolution; + + canvas.width = Math.ceil(crop.width); + canvas.height = Math.ceil(crop.height); + + context.save(); + context.globalCompositeOperation = 'copy'; + context.drawImage( + texture.baseTexture.getDrawableSource(), + crop.x, + crop.y, + crop.width, + crop.height, + 0, + 0, + crop.width, + crop.height + ); + context.restore(); + + const rgbValues = hex2rgb(color); + const r = rgbValues[0]; + const g = rgbValues[1]; + const b = rgbValues[2]; + + const pixelData = context.getImageData(0, 0, crop.width, crop.height); + + const pixels = pixelData.data; + + for (let i = 0; i < pixels.length; i += 4) + { + pixels[i + 0] *= r; + pixels[i + 1] *= g; + pixels[i + 2] *= b; + } + + context.putImageData(pixelData, 0, 0); + }, + + /** + * Rounds the specified color according to the canvasUtils.cacheStepsPerColorChannel. + * + * @memberof PIXI.canvasUtils + * @param {number} color - the color to round, should be a hex color + * @return {number} The rounded color. + */ + roundColor: (color) => + { + const step = canvasUtils.cacheStepsPerColorChannel; + + const rgbValues = hex2rgb(color); + + rgbValues[0] = Math.min(255, (rgbValues[0] / step) * step); + rgbValues[1] = Math.min(255, (rgbValues[1] / step) * step); + rgbValues[2] = Math.min(255, (rgbValues[2] / step) * step); + + return rgb2hex(rgbValues); + }, + + /** + * Number of steps which will be used as a cap when rounding colors. + * + * @memberof PIXI.canvasUtils + * @type {number} + */ + cacheStepsPerColorChannel: 8, + + /** + * Tint cache boolean flag. + * + * @memberof PIXI.canvasUtils + * @type {boolean} + */ + convertTintToImage: false, + + /** + * Whether or not the Canvas BlendModes are supported, consequently the ability to tint using the multiply method. + * + * @memberof PIXI.canvasUtils + * @type {boolean} + */ + canUseMultiply: canUseNewCanvasBlendModes(), + + /** + * The tinting method that will be used. + * + * @memberof PIXI.canvasUtils + * @type {Function} + */ + tintMethod: () => + { // jslint-disable no-empty-function + + }, +}; + +canvasUtils.tintMethod = canvasUtils.canUseMultiply ? canvasUtils.tintWithMultiply : canvasUtils.tintWithPerPixel; diff --git a/packages/canvas/canvas-renderer/src/index.js b/packages/canvas/canvas-renderer/src/index.js index 80987c3..71bc5ee 100644 --- a/packages/canvas/canvas-renderer/src/index.js +++ b/packages/canvas/canvas-renderer/src/index.js @@ -1,6 +1,6 @@ export * from './CanvasRenderer'; export * from './utils/canUseNewCanvasBlendModes'; -export * from './CanvasTinter'; +export * from './canvasUtils'; import './Renderer'; import './BaseTexture'; diff --git a/packages/canvas/canvas-sprite-tiling/src/TilingSprite.js b/packages/canvas/canvas-sprite-tiling/src/TilingSprite.js index 4eac1d7..fd8857f 100644 --- a/packages/canvas/canvas-sprite-tiling/src/TilingSprite.js +++ b/packages/canvas/canvas-sprite-tiling/src/TilingSprite.js @@ -1,5 +1,5 @@ import { TilingSprite } from '@pixi/sprite-tiling'; -import { CanvasTinter } from '@pixi/canvas-renderer'; +import { canvasUtils } from '@pixi/canvas-renderer'; import { CanvasRenderTarget } from '@pixi/utils'; /** @@ -40,7 +40,7 @@ // Tint the tiling sprite if (this.tint !== 0xFFFFFF) { - this._tintedCanvas = CanvasTinter.getTintedCanvas(this, this.tint); + this._tintedCanvas = canvasUtils.getTintedCanvas(this, this.tint); tempCanvas.context.drawImage(this._tintedCanvas, 0, 0); } else diff --git a/packages/canvas/canvas-sprite/src/CanvasSpriteRenderer.js b/packages/canvas/canvas-sprite/src/CanvasSpriteRenderer.js index 507571c..3cdea2a 100644 --- a/packages/canvas/canvas-sprite/src/CanvasSpriteRenderer.js +++ b/packages/canvas/canvas-sprite/src/CanvasSpriteRenderer.js @@ -1,6 +1,6 @@ import { SCALE_MODES, BLEND_MODES } from '@pixi/constants'; -import { Matrix, GroupD8 } from '@pixi/math'; -import { CanvasTinter } from '@pixi/canvas-renderer'; +import { Matrix, groupD8 } from '@pixi/math'; +import { canvasUtils } from '@pixi/canvas-renderer'; const canvasRenderWorldTransform = new Matrix(); @@ -96,7 +96,7 @@ { wt.copyTo(canvasRenderWorldTransform); wt = canvasRenderWorldTransform; - GroupD8.matrixAppendRotationInv(wt, texture.rotate, dx, dy); + groupD8.matrixAppendRotationInv(wt, texture.rotate, dx, dy); // the anchor has already been applied above, so lets set it to zero dx = 0; dy = 0; @@ -155,7 +155,7 @@ sprite._cachedTint = sprite.tint; // TODO clean up caching - how to clean up the caches? - sprite._tintedCanvas = CanvasTinter.getTintedCanvas(sprite, sprite.tint); + sprite._tintedCanvas = canvasUtils.getTintedCanvas(sprite, sprite.tint); } context.drawImage( diff --git a/packages/core/src/textures/Texture.js b/packages/core/src/textures/Texture.js index ea5c843..107b61e 100644 --- a/packages/core/src/textures/Texture.js +++ b/packages/core/src/textures/Texture.js @@ -45,7 +45,7 @@ * @param {PIXI.Rectangle} [frame] - The rectangle frame of the texture to show * @param {PIXI.Rectangle} [orig] - The area of original texture * @param {PIXI.Rectangle} [trim] - Trimmed rectangle of original texture - * @param {number} [rotate] - indicates how the texture was rotated by texture packer. See {@link PIXI.GroupD8} + * @param {number} [rotate] - indicates how the texture was rotated by texture packer. See {@link PIXI.groupD8} * @param {PIXI.Point} [anchor] - Default anchor point used for sprite placement / rotation */ constructor(baseTexture, frame, orig, trim, rotate, anchor) @@ -563,7 +563,7 @@ * set to 2 to compensate for texture packer rotation * set to 6 to compensate for spine packer rotation * can be used to rotate or mirror sprites - * See {@link PIXI.GroupD8} for explanation + * See {@link PIXI.groupD8} for explanation * * @member {number} */ diff --git a/packages/core/src/textures/TextureUvs.js b/packages/core/src/textures/TextureUvs.js index 322e973..735d928 100644 --- a/packages/core/src/textures/TextureUvs.js +++ b/packages/core/src/textures/TextureUvs.js @@ -1,4 +1,4 @@ -import { GroupD8 } from '@pixi/math'; +import { groupD8 } from '@pixi/math'; /** * Stores a texture's frame in UV coordinates, in @@ -85,7 +85,7 @@ * @protected * @param {PIXI.Rectangle} frame - The frame of the texture * @param {PIXI.Rectangle} baseFrame - The base frame of the texture - * @param {number} rotate - Rotation of frame, see {@link PIXI.GroupD8} + * @param {number} rotate - Rotation of frame, see {@link PIXI.groupD8} */ set(frame, baseFrame, rotate) { @@ -102,21 +102,21 @@ const cX = (frame.x / tw) + w2; const cY = (frame.y / th) + h2; - rotate = GroupD8.add(rotate, GroupD8.NW); // NW is top-left corner - this.x0 = cX + (w2 * GroupD8.uX(rotate)); - this.y0 = cY + (h2 * GroupD8.uY(rotate)); + rotate = groupD8.add(rotate, groupD8.NW); // NW is top-left corner + this.x0 = cX + (w2 * groupD8.uX(rotate)); + this.y0 = cY + (h2 * groupD8.uY(rotate)); - rotate = GroupD8.add(rotate, 2); // rotate 90 degrees clockwise - this.x1 = cX + (w2 * GroupD8.uX(rotate)); - this.y1 = cY + (h2 * GroupD8.uY(rotate)); + rotate = groupD8.add(rotate, 2); // rotate 90 degrees clockwise + this.x1 = cX + (w2 * groupD8.uX(rotate)); + this.y1 = cY + (h2 * groupD8.uY(rotate)); - rotate = GroupD8.add(rotate, 2); - this.x2 = cX + (w2 * GroupD8.uX(rotate)); - this.y2 = cY + (h2 * GroupD8.uY(rotate)); + rotate = groupD8.add(rotate, 2); + this.x2 = cX + (w2 * groupD8.uX(rotate)); + this.y2 = cY + (h2 * groupD8.uY(rotate)); - rotate = GroupD8.add(rotate, 2); - this.x3 = cX + (w2 * GroupD8.uX(rotate)); - this.y3 = cY + (h2 * GroupD8.uY(rotate)); + rotate = groupD8.add(rotate, 2); + this.x3 = cX + (w2 * groupD8.uX(rotate)); + this.y3 = cY + (h2 * groupD8.uY(rotate)); } else { diff --git a/packages/math/src/GroupD8.js b/packages/math/src/GroupD8.js deleted file mode 100644 index 513cea8..0000000 --- a/packages/math/src/GroupD8.js +++ /dev/null @@ -1,403 +0,0 @@ -// Your friendly neighbour https://en.wikipedia.org/wiki/Dihedral_group -// -// This file implements the dihedral group of order 16, also called -// of degree 8. That's why its called GroupD8. - -import { Matrix } from './Matrix'; - -/* - * Transform matrix for operation n is: - * | ux | vx | - * | uy | vy | - */ - -const ux = [1, 1, 0, -1, -1, -1, 0, 1, 1, 1, 0, -1, -1, -1, 0, 1]; -const uy = [0, 1, 1, 1, 0, -1, -1, -1, 0, 1, 1, 1, 0, -1, -1, -1]; -const vx = [0, -1, -1, -1, 0, 1, 1, 1, 0, 1, 1, 1, 0, -1, -1, -1]; -const vy = [1, 1, 0, -1, -1, -1, 0, 1, -1, -1, 0, 1, 1, 1, 0, -1]; - -/** - * [Cayley Table]{@link https://en.wikipedia.org/wiki/Cayley_table} - * for the composition of each rotation in the dihederal group D8. - * - * @type number[][] - * @private - */ -const rotationCayley = []; - -/** - * Matrices for each `GD8Symmetry` rotation. - * - * @type Matrix[] - * @private - */ -const rotationMatrices = []; - -/* - * Alias for {@code Math.sign}. - */ -const signum = Math.sign; - -/* - * Initializes `rotationCayley` and `rotationMatrices`. It is called - * only once below. - */ -function init() -{ - for (let i = 0; i < 16; i++) - { - const row = []; - - rotationCayley.push(row); - - for (let j = 0; j < 16; j++) - { - /* Multiplies rotation matrices i and j. */ - const _ux = signum((ux[i] * ux[j]) + (vx[i] * uy[j])); - const _uy = signum((uy[i] * ux[j]) + (vy[i] * uy[j])); - const _vx = signum((ux[i] * vx[j]) + (vx[i] * vy[j])); - const _vy = signum((uy[i] * vx[j]) + (vy[i] * vy[j])); - - /* Finds rotation matrix matching the product and pushes it. */ - for (let k = 0; k < 16; k++) - { - if (ux[k] === _ux && uy[k] === _uy - && vx[k] === _vx && vy[k] === _vy) - { - row.push(k); - break; - } - } - } - } - - for (let i = 0; i < 16; i++) - { - const mat = new Matrix(); - - mat.set(ux[i], uy[i], vx[i], vy[i], 0, 0); - rotationMatrices.push(mat); - } -} - -init(); - -/** - * @memberof PIXI - * @typedef {number} GD8Symmetry - * @see PIXI.GroupD8 - */ - -/** - * Implements the dihedral group D8, which is similar to - * [group D4]{@link http://mathworld.wolfram.com/DihedralGroupD4.html}; - * D8 is the same but with diagonals, and it is used for texture - * rotations. - * - * The directions the U- and V- axes after rotation - * of an angle of `a: GD8Constant` are the vectors `(uX(a), uY(a))` - * and `(vX(a), vY(a))`. These aren't necessarily unit vectors. - * - * **Origin:**
- * This is the small part of gameofbombs.com portal system. It works. - * - * @see PIXI.GroupD8.E - * @see PIXI.GroupD8.SE - * @see PIXI.GroupD8.S - * @see PIXI.GroupD8.SW - * @see PIXI.GroupD8.W - * @see PIXI.GroupD8.NW - * @see PIXI.GroupD8.N - * @see PIXI.GroupD8.NE - * @author Ivan @ivanpopelyshev - * @namespace PIXI.GroupD8 - * @memberof PIXI - */ -export const GroupD8 = { - /** - * | Rotation | Direction | - * |----------|-----------| - * | 0° | East | - * - * @memberof PIXI.GroupD8 - * @constant {PIXI.GD8Symmetry} - */ - E: 0, - - /** - * | Rotation | Direction | - * |----------|-----------| - * | 45°↻ | Southeast | - * - * @memberof PIXI.GroupD8 - * @constant {PIXI.GD8Symmetry} - */ - SE: 1, - - /** - * | Rotation | Direction | - * |----------|-----------| - * | 90°↻ | South | - * - * @memberof PIXI.GroupD8 - * @constant {PIXI.GD8Symmetry} - */ - S: 2, - - /** - * | Rotation | Direction | - * |----------|-----------| - * | 135°↻ | Southwest | - * - * @memberof PIXI.GroupD8 - * @constant {PIXI.GD8Symmetry} - */ - SW: 3, - - /** - * | Rotation | Direction | - * |----------|-----------| - * | 180° | West | - * - * @memberof PIXI.GroupD8 - * @constant {PIXI.GD8Symmetry} - */ - W: 4, - - /** - * | Rotation | Direction | - * |-------------|--------------| - * | -135°/225°↻ | Northwest | - * - * @memberof PIXI.GroupD8 - * @constant {PIXI.GD8Symmetry} - */ - NW: 5, - - /** - * | Rotation | Direction | - * |-------------|--------------| - * | -90°/270°↻ | North | - * - * @memberof PIXI.GroupD8 - * @constant {PIXI.GD8Symmetry} - */ - N: 6, - - /** - * | Rotation | Direction | - * |-------------|--------------| - * | -45°/315°↻ | Northeast | - * - * @memberof PIXI.GroupD8 - * @constant {PIXI.GD8Symmetry} - */ - NE: 7, - - /** - * Reflection about Y-axis. - * - * @memberof PIXI.GroupD8 - * @constant {PIXI.GD8Symmetry} - */ - MIRROR_VERTICAL: 8, - - /** - * Reflection about the main diagonal. - * - * @memberof PIXI.GroupD8 - * @constant {PIXI.GD8Symmetry} - */ - MAIN_DIAGONAL: 10, - - /** - * Reflection about X-axis. - * - * @memberof PIXI.GroupD8 - * @constant {PIXI.GD8Symmetry} - */ - MIRROR_HORIZONTAL: 12, - - /** - * Reflection about reverse diagonal. - * - * @memberof PIXI.GroupD8 - * @constant {PIXI.GD8Symmetry} - */ - REVERSE_DIAGONAL: 14, - - /** - * @memberof PIXI.GroupD8 - * @param {PIXI.GD8Symmetry} ind - sprite rotation angle. - * @return {PIXI.GD8Symmetry} The X-component of the U-axis - * after rotating the axes. - */ - uX: (ind) => ux[ind], - - /** - * @memberof PIXI.GroupD8 - * @param {PIXI.GD8Symmetry} ind - sprite rotation angle. - * @return {PIXI.GD8Symmetry} The Y-component of the U-axis - * after rotating the axes. - */ - uY: (ind) => uy[ind], - - /** - * @memberof PIXI.GroupD8 - * @param {PIXI.GD8Symmetry} ind - sprite rotation angle. - * @return {PIXI.GD8Symmetry} The X-component of the V-axis - * after rotating the axes. - */ - vX: (ind) => vx[ind], - - /** - * @memberof PIXI.GroupD8 - * @param {PIXI.GD8Symmetry} ind - sprite rotation angle. - * @return {PIXI.GD8Symmetry} The Y-component of the V-axis - * after rotating the axes. - */ - vY: (ind) => vy[ind], - - /** - * @memberof PIXI.GroupD8 - * @param {PIXI.GD8Symmetry} rotation - symmetry whose opposite - * is needed. Only rotations have opposite symmetries while - * reflections don't. - * @return {PIXI.GD8Symmetry} The opposite symmetry of `rotation` - */ - inv: (rotation) => - { - if (rotation & 8)// true only if between 8 & 15 (reflections) - { - return rotation & 15;// or rotation % 16 - } - - return (-rotation) & 7;// or (8 - rotation) % 8 - }, - - /** - * Composes the two D8 operations. - * - * Taking `^` as reflection: - * - * | | E=0 | S=2 | W=4 | N=6 | E^=8 | S^=10 | W^=12 | N^=14 | - * |-------|-----|-----|-----|-----|------|-------|-------|-------| - * | E=0 | E | S | W | N | E^ | S^ | W^ | N^ | - * | S=2 | S | W | N | E | S^ | W^ | N^ | E^ | - * | W=4 | W | N | E | S | W^ | N^ | E^ | S^ | - * | N=6 | N | E | S | W | N^ | E^ | S^ | W^ | - * | E^=8 | E^ | N^ | W^ | S^ | E | N | W | S | - * | S^=10 | S^ | E^ | N^ | W^ | S | E | N | W | - * | W^=12 | W^ | S^ | E^ | N^ | W | S | E | N | - * | N^=14 | N^ | W^ | S^ | E^ | N | W | S | E | - * - * [This is a Cayley table]{@link https://en.wikipedia.org/wiki/Cayley_table} - * @memberof PIXI.GroupD8 - * @param {PIXI.GD8Symmetry} rotationSecond - Second operation, which - * is the row in the above cayley table. - * @param {PIXI.GD8Symmetry} rotationFirst - First operation, which - * is the column in the above cayley table. - * @return {PIXI.GD8Symmetry} Composed operation - */ - add: (rotationSecond, rotationFirst) => ( - rotationCayley[rotationSecond][rotationFirst] - ), - - /** - * Reverse of `add`. - * - * @memberof PIXI.GroupD8 - * @param {PIXI.GD8Symmetry} rotationSecond - Second operation - * @param {PIXI.GD8Symmetry} rotationFirst - First operation - * @return {PIXI.GD8Symmetry} Result - */ - sub: (rotationSecond, rotationFirst) => ( - rotationCayley[rotationSecond][GroupD8.inv(rotationFirst)] - ), - - /** - * Adds 180 degrees to rotation, which is a commutative - * operation. - * - * @memberof PIXI.GroupD8 - * @param {number} rotation - The number to rotate. - * @returns {number} Rotated number - */ - rotate180: (rotation) => rotation ^ 4, - - /** - * Checks if the rotation angle is vertical, i.e. south - * or north. It doesn't work for reflections. - * - * @memberof PIXI.GroupD8 - * @param {PIXI.GD8Symmetry} rotation - The number to check. - * @returns {boolean} Whether or not the direction is vertical - */ - isVertical: (rotation) => (rotation & 3) === 2, // rotation % 4 === 2 - - /** - * Approximates the vector `V(dx,dy)` into one of the - * eight directions provided by `GroupD8`. - * - * @memberof PIXI.GroupD8 - * @param {number} dx - X-component of the vector - * @param {number} dy - Y-component of the vector - * @return {PIXI.GD8Symmetry} Approximation of the vector into - * one of the eight symmetries. - */ - byDirection: (dx, dy) => - { - if (Math.abs(dx) * 2 <= Math.abs(dy)) - { - if (dy >= 0) - { - return GroupD8.S; - } - - return GroupD8.N; - } - else if (Math.abs(dy) * 2 <= Math.abs(dx)) - { - if (dx > 0) - { - return GroupD8.E; - } - - return GroupD8.W; - } - else if (dy > 0) - { - if (dx > 0) - { - return GroupD8.SE; - } - - return GroupD8.SW; - } - else if (dx > 0) - { - return GroupD8.NE; - } - - return GroupD8.NW; - }, - - /** - * Helps sprite to compensate texture packer rotation. - * - * @memberof PIXI.GroupD8 - * @param {PIXI.Matrix} matrix - sprite world matrix - * @param {PIXI.GD8Symmetry} rotation - The rotation factor to use. - * @param {number} tx - sprite anchoring - * @param {number} ty - sprite anchoring - */ - matrixAppendRotationInv: (matrix, rotation, tx = 0, ty = 0) => - { - // Packer used "rotation", we use "inv(rotation)" - const mat = rotationMatrices[GroupD8.inv(rotation)]; - - mat.tx = tx; - mat.ty = ty; - matrix.append(mat); - }, -}; diff --git a/packages/math/src/groupD8.js b/packages/math/src/groupD8.js new file mode 100644 index 0000000..8b9f542 --- /dev/null +++ b/packages/math/src/groupD8.js @@ -0,0 +1,403 @@ +// Your friendly neighbour https://en.wikipedia.org/wiki/Dihedral_group +// +// This file implements the dihedral group of order 16, also called +// of degree 8. That's why its called groupD8. + +import { Matrix } from './Matrix'; + +/* + * Transform matrix for operation n is: + * | ux | vx | + * | uy | vy | + */ + +const ux = [1, 1, 0, -1, -1, -1, 0, 1, 1, 1, 0, -1, -1, -1, 0, 1]; +const uy = [0, 1, 1, 1, 0, -1, -1, -1, 0, 1, 1, 1, 0, -1, -1, -1]; +const vx = [0, -1, -1, -1, 0, 1, 1, 1, 0, 1, 1, 1, 0, -1, -1, -1]; +const vy = [1, 1, 0, -1, -1, -1, 0, 1, -1, -1, 0, 1, 1, 1, 0, -1]; + +/** + * [Cayley Table]{@link https://en.wikipedia.org/wiki/Cayley_table} + * for the composition of each rotation in the dihederal group D8. + * + * @type number[][] + * @private + */ +const rotationCayley = []; + +/** + * Matrices for each `GD8Symmetry` rotation. + * + * @type Matrix[] + * @private + */ +const rotationMatrices = []; + +/* + * Alias for {@code Math.sign}. + */ +const signum = Math.sign; + +/* + * Initializes `rotationCayley` and `rotationMatrices`. It is called + * only once below. + */ +function init() +{ + for (let i = 0; i < 16; i++) + { + const row = []; + + rotationCayley.push(row); + + for (let j = 0; j < 16; j++) + { + /* Multiplies rotation matrices i and j. */ + const _ux = signum((ux[i] * ux[j]) + (vx[i] * uy[j])); + const _uy = signum((uy[i] * ux[j]) + (vy[i] * uy[j])); + const _vx = signum((ux[i] * vx[j]) + (vx[i] * vy[j])); + const _vy = signum((uy[i] * vx[j]) + (vy[i] * vy[j])); + + /* Finds rotation matrix matching the product and pushes it. */ + for (let k = 0; k < 16; k++) + { + if (ux[k] === _ux && uy[k] === _uy + && vx[k] === _vx && vy[k] === _vy) + { + row.push(k); + break; + } + } + } + } + + for (let i = 0; i < 16; i++) + { + const mat = new Matrix(); + + mat.set(ux[i], uy[i], vx[i], vy[i], 0, 0); + rotationMatrices.push(mat); + } +} + +init(); + +/** + * @memberof PIXI + * @typedef {number} GD8Symmetry + * @see PIXI.groupD8 + */ + +/** + * Implements the dihedral group D8, which is similar to + * [group D4]{@link http://mathworld.wolfram.com/DihedralGroupD4.html}; + * D8 is the same but with diagonals, and it is used for texture + * rotations. + * + * The directions the U- and V- axes after rotation + * of an angle of `a: GD8Constant` are the vectors `(uX(a), uY(a))` + * and `(vX(a), vY(a))`. These aren't necessarily unit vectors. + * + * **Origin:**
+ * This is the small part of gameofbombs.com portal system. It works. + * + * @see PIXI.groupD8.E + * @see PIXI.groupD8.SE + * @see PIXI.groupD8.S + * @see PIXI.groupD8.SW + * @see PIXI.groupD8.W + * @see PIXI.groupD8.NW + * @see PIXI.groupD8.N + * @see PIXI.groupD8.NE + * @author Ivan @ivanpopelyshev + * @namespace PIXI.groupD8 + * @memberof PIXI + */ +export const groupD8 = { + /** + * | Rotation | Direction | + * |----------|-----------| + * | 0° | East | + * + * @memberof PIXI.groupD8 + * @constant {PIXI.GD8Symmetry} + */ + E: 0, + + /** + * | Rotation | Direction | + * |----------|-----------| + * | 45°↻ | Southeast | + * + * @memberof PIXI.groupD8 + * @constant {PIXI.GD8Symmetry} + */ + SE: 1, + + /** + * | Rotation | Direction | + * |----------|-----------| + * | 90°↻ | South | + * + * @memberof PIXI.groupD8 + * @constant {PIXI.GD8Symmetry} + */ + S: 2, + + /** + * | Rotation | Direction | + * |----------|-----------| + * | 135°↻ | Southwest | + * + * @memberof PIXI.groupD8 + * @constant {PIXI.GD8Symmetry} + */ + SW: 3, + + /** + * | Rotation | Direction | + * |----------|-----------| + * | 180° | West | + * + * @memberof PIXI.groupD8 + * @constant {PIXI.GD8Symmetry} + */ + W: 4, + + /** + * | Rotation | Direction | + * |-------------|--------------| + * | -135°/225°↻ | Northwest | + * + * @memberof PIXI.groupD8 + * @constant {PIXI.GD8Symmetry} + */ + NW: 5, + + /** + * | Rotation | Direction | + * |-------------|--------------| + * | -90°/270°↻ | North | + * + * @memberof PIXI.groupD8 + * @constant {PIXI.GD8Symmetry} + */ + N: 6, + + /** + * | Rotation | Direction | + * |-------------|--------------| + * | -45°/315°↻ | Northeast | + * + * @memberof PIXI.groupD8 + * @constant {PIXI.GD8Symmetry} + */ + NE: 7, + + /** + * Reflection about Y-axis. + * + * @memberof PIXI.groupD8 + * @constant {PIXI.GD8Symmetry} + */ + MIRROR_VERTICAL: 8, + + /** + * Reflection about the main diagonal. + * + * @memberof PIXI.groupD8 + * @constant {PIXI.GD8Symmetry} + */ + MAIN_DIAGONAL: 10, + + /** + * Reflection about X-axis. + * + * @memberof PIXI.groupD8 + * @constant {PIXI.GD8Symmetry} + */ + MIRROR_HORIZONTAL: 12, + + /** + * Reflection about reverse diagonal. + * + * @memberof PIXI.groupD8 + * @constant {PIXI.GD8Symmetry} + */ + REVERSE_DIAGONAL: 14, + + /** + * @memberof PIXI.groupD8 + * @param {PIXI.GD8Symmetry} ind - sprite rotation angle. + * @return {PIXI.GD8Symmetry} The X-component of the U-axis + * after rotating the axes. + */ + uX: (ind) => ux[ind], + + /** + * @memberof PIXI.groupD8 + * @param {PIXI.GD8Symmetry} ind - sprite rotation angle. + * @return {PIXI.GD8Symmetry} The Y-component of the U-axis + * after rotating the axes. + */ + uY: (ind) => uy[ind], + + /** + * @memberof PIXI.groupD8 + * @param {PIXI.GD8Symmetry} ind - sprite rotation angle. + * @return {PIXI.GD8Symmetry} The X-component of the V-axis + * after rotating the axes. + */ + vX: (ind) => vx[ind], + + /** + * @memberof PIXI.groupD8 + * @param {PIXI.GD8Symmetry} ind - sprite rotation angle. + * @return {PIXI.GD8Symmetry} The Y-component of the V-axis + * after rotating the axes. + */ + vY: (ind) => vy[ind], + + /** + * @memberof PIXI.groupD8 + * @param {PIXI.GD8Symmetry} rotation - symmetry whose opposite + * is needed. Only rotations have opposite symmetries while + * reflections don't. + * @return {PIXI.GD8Symmetry} The opposite symmetry of `rotation` + */ + inv: (rotation) => + { + if (rotation & 8)// true only if between 8 & 15 (reflections) + { + return rotation & 15;// or rotation % 16 + } + + return (-rotation) & 7;// or (8 - rotation) % 8 + }, + + /** + * Composes the two D8 operations. + * + * Taking `^` as reflection: + * + * | | E=0 | S=2 | W=4 | N=6 | E^=8 | S^=10 | W^=12 | N^=14 | + * |-------|-----|-----|-----|-----|------|-------|-------|-------| + * | E=0 | E | S | W | N | E^ | S^ | W^ | N^ | + * | S=2 | S | W | N | E | S^ | W^ | N^ | E^ | + * | W=4 | W | N | E | S | W^ | N^ | E^ | S^ | + * | N=6 | N | E | S | W | N^ | E^ | S^ | W^ | + * | E^=8 | E^ | N^ | W^ | S^ | E | N | W | S | + * | S^=10 | S^ | E^ | N^ | W^ | S | E | N | W | + * | W^=12 | W^ | S^ | E^ | N^ | W | S | E | N | + * | N^=14 | N^ | W^ | S^ | E^ | N | W | S | E | + * + * [This is a Cayley table]{@link https://en.wikipedia.org/wiki/Cayley_table} + * @memberof PIXI.groupD8 + * @param {PIXI.GD8Symmetry} rotationSecond - Second operation, which + * is the row in the above cayley table. + * @param {PIXI.GD8Symmetry} rotationFirst - First operation, which + * is the column in the above cayley table. + * @return {PIXI.GD8Symmetry} Composed operation + */ + add: (rotationSecond, rotationFirst) => ( + rotationCayley[rotationSecond][rotationFirst] + ), + + /** + * Reverse of `add`. + * + * @memberof PIXI.groupD8 + * @param {PIXI.GD8Symmetry} rotationSecond - Second operation + * @param {PIXI.GD8Symmetry} rotationFirst - First operation + * @return {PIXI.GD8Symmetry} Result + */ + sub: (rotationSecond, rotationFirst) => ( + rotationCayley[rotationSecond][groupD8.inv(rotationFirst)] + ), + + /** + * Adds 180 degrees to rotation, which is a commutative + * operation. + * + * @memberof PIXI.groupD8 + * @param {number} rotation - The number to rotate. + * @returns {number} Rotated number + */ + rotate180: (rotation) => rotation ^ 4, + + /** + * Checks if the rotation angle is vertical, i.e. south + * or north. It doesn't work for reflections. + * + * @memberof PIXI.groupD8 + * @param {PIXI.GD8Symmetry} rotation - The number to check. + * @returns {boolean} Whether or not the direction is vertical + */ + isVertical: (rotation) => (rotation & 3) === 2, // rotation % 4 === 2 + + /** + * Approximates the vector `V(dx,dy)` into one of the + * eight directions provided by `groupD8`. + * + * @memberof PIXI.groupD8 + * @param {number} dx - X-component of the vector + * @param {number} dy - Y-component of the vector + * @return {PIXI.GD8Symmetry} Approximation of the vector into + * one of the eight symmetries. + */ + byDirection: (dx, dy) => + { + if (Math.abs(dx) * 2 <= Math.abs(dy)) + { + if (dy >= 0) + { + return groupD8.S; + } + + return groupD8.N; + } + else if (Math.abs(dy) * 2 <= Math.abs(dx)) + { + if (dx > 0) + { + return groupD8.E; + } + + return groupD8.W; + } + else if (dy > 0) + { + if (dx > 0) + { + return groupD8.SE; + } + + return groupD8.SW; + } + else if (dx > 0) + { + return groupD8.NE; + } + + return groupD8.NW; + }, + + /** + * Helps sprite to compensate texture packer rotation. + * + * @memberof PIXI.groupD8 + * @param {PIXI.Matrix} matrix - sprite world matrix + * @param {PIXI.GD8Symmetry} rotation - The rotation factor to use. + * @param {number} tx - sprite anchoring + * @param {number} ty - sprite anchoring + */ + matrixAppendRotationInv: (matrix, rotation, tx = 0, ty = 0) => + { + // Packer used "rotation", we use "inv(rotation)" + const mat = rotationMatrices[groupD8.inv(rotation)]; + + mat.tx = tx; + mat.ty = ty; + matrix.append(mat); + }, +}; diff --git a/bundles/pixi.js-legacy/src/index.js b/bundles/pixi.js-legacy/src/index.js index f1598a1..df6b157 100644 --- a/bundles/pixi.js-legacy/src/index.js +++ b/bundles/pixi.js-legacy/src/index.js @@ -1,5 +1,5 @@ import { accessibility, interaction, prepare, extract } from 'pixi.js'; -import { CanvasRenderer, CanvasTinter } from '@pixi/canvas-renderer'; +import { CanvasRenderer, canvasUtils } from '@pixi/canvas-renderer'; import { CanvasMeshRenderer } from '@pixi/canvas-mesh'; import { CanvasGraphicsRenderer } from '@pixi/canvas-graphics'; import { CanvasSpriteRenderer } from '@pixi/canvas-sprite'; @@ -29,5 +29,5 @@ CanvasGraphicsRenderer, CanvasMeshRenderer, CanvasSpriteRenderer, - CanvasTinter, + canvasUtils, }; diff --git a/bundles/pixi.js/src/useDeprecated.js b/bundles/pixi.js/src/useDeprecated.js index 12f00d4..ab88092 100644 --- a/bundles/pixi.js/src/useDeprecated.js +++ b/bundles/pixi.js/src/useDeprecated.js @@ -138,6 +138,34 @@ return PIXI.systems.FilterSystem; }, }, + + /** + * @namespace PIXI.CanvasTinter + * @see PIXI.canvasUtils + * @deprecated since 5.2.0 + */ + CanvasTinter: { + get() + { + deprecation('5.2.0', 'PIXI.CanvasTinter namespace has moved to PIXI.canvasUtils'); + + return PIXI.canvasUtils; + }, + }, + + /** + * @namespace PIXI.GroupD8 + * @see PIXI.groupD8 + * @deprecated since 5.2.0 + */ + GroupD8: { + get() + { + deprecation('5.2.0', 'PIXI.GroupD8 namespace has moved to PIXI.groupD8'); + + return PIXI.groupD8; + }, + }, }); /** diff --git a/packages/canvas/canvas-mesh/src/NineSlicePlane.js b/packages/canvas/canvas-mesh/src/NineSlicePlane.js index dccfd79..6e2f055 100644 --- a/packages/canvas/canvas-mesh/src/NineSlicePlane.js +++ b/packages/canvas/canvas-mesh/src/NineSlicePlane.js @@ -1,4 +1,4 @@ -import { CanvasTinter } from '@pixi/canvas-renderer'; +import { canvasUtils } from '@pixi/canvas-renderer'; import { NineSlicePlane } from '@pixi/mesh-extras'; /** @@ -50,7 +50,7 @@ this._cachedTint = this.tint; - this._tintedCanvas = CanvasTinter.getTintedCanvas(this, this.tint); + this._tintedCanvas = canvasUtils.getTintedCanvas(this, this.tint); } } diff --git a/packages/canvas/canvas-renderer/src/CanvasTinter.js b/packages/canvas/canvas-renderer/src/CanvasTinter.js deleted file mode 100644 index bfec51a..0000000 --- a/packages/canvas/canvas-renderer/src/CanvasTinter.js +++ /dev/null @@ -1,288 +0,0 @@ -import { hex2rgb, rgb2hex } from '@pixi/utils'; -import { canUseNewCanvasBlendModes } from './utils/canUseNewCanvasBlendModes'; - -/** - * Utility methods for Sprite/Texture tinting. - * - * Tinting with the CanvasRenderer involves creating a new canvas to use as a texture, - * so be aware of the performance implications. - * - * @namespace PIXI.CanvasTinter - * @memberof PIXI - */ -export const CanvasTinter = { - /** - * Basically this method just needs a sprite and a color and tints the sprite with the given color. - * - * @memberof PIXI.CanvasTinter - * @param {PIXI.Sprite} sprite - the sprite to tint - * @param {number} color - the color to use to tint the sprite with - * @return {HTMLCanvasElement} The tinted canvas - */ - getTintedCanvas: (sprite, color) => - { - const texture = sprite.texture; - - color = CanvasTinter.roundColor(color); - - const stringColor = `#${(`00000${(color | 0).toString(16)}`).substr(-6)}`; - - texture.tintCache = texture.tintCache || {}; - - const cachedCanvas = texture.tintCache[stringColor]; - - let canvas; - - if (cachedCanvas) - { - if (cachedCanvas.tintId === texture._updateID) - { - return texture.tintCache[stringColor]; - } - - canvas = texture.tintCache[stringColor]; - } - else - { - canvas = CanvasTinter.canvas || document.createElement('canvas'); - } - - CanvasTinter.tintMethod(texture, color, canvas); - - canvas.tintId = texture._updateID; - - if (CanvasTinter.convertTintToImage) - { - // is this better? - const tintImage = new Image(); - - tintImage.src = canvas.toDataURL(); - - texture.tintCache[stringColor] = tintImage; - } - else - { - texture.tintCache[stringColor] = canvas; - // if we are not converting the texture to an image then we need to lose the reference to the canvas - CanvasTinter.canvas = null; - } - - return canvas; - }, - - /** - * Tint a texture using the 'multiply' operation. - * - * @memberof PIXI.CanvasTinter - * @param {PIXI.Texture} texture - the texture to tint - * @param {number} color - the color to use to tint the sprite with - * @param {HTMLCanvasElement} canvas - the current canvas - */ - tintWithMultiply: (texture, color, canvas) => - { - const context = canvas.getContext('2d'); - const crop = texture._frame.clone(); - const resolution = texture.baseTexture.resolution; - - crop.x *= resolution; - crop.y *= resolution; - crop.width *= resolution; - crop.height *= resolution; - - canvas.width = Math.ceil(crop.width); - canvas.height = Math.ceil(crop.height); - - context.save(); - context.fillStyle = `#${(`00000${(color | 0).toString(16)}`).substr(-6)}`; - - context.fillRect(0, 0, crop.width, crop.height); - - context.globalCompositeOperation = 'multiply'; - - const source = texture.baseTexture.getDrawableSource(); - - context.drawImage( - source, - crop.x, - crop.y, - crop.width, - crop.height, - 0, - 0, - crop.width, - crop.height - ); - - context.globalCompositeOperation = 'destination-atop'; - - context.drawImage( - source, - crop.x, - crop.y, - crop.width, - crop.height, - 0, - 0, - crop.width, - crop.height - ); - context.restore(); - }, - - /** - * Tint a texture using the 'overlay' operation. - * - * @memberof PIXI.CanvasTinter - * @param {PIXI.Texture} texture - the texture to tint - * @param {number} color - the color to use to tint the sprite with - * @param {HTMLCanvasElement} canvas - the current canvas - */ - tintWithOverlay(texture, color, canvas) - { - const context = canvas.getContext('2d'); - const crop = texture._frame.clone(); - const resolution = texture.baseTexture.resolution; - - crop.x *= resolution; - crop.y *= resolution; - crop.width *= resolution; - crop.height *= resolution; - - canvas.width = Math.ceil(crop.width); - canvas.height = Math.ceil(crop.height); - - context.save(); - context.globalCompositeOperation = 'copy'; - context.fillStyle = `#${(`00000${(color | 0).toString(16)}`).substr(-6)}`; - context.fillRect(0, 0, crop.width, crop.height); - - context.globalCompositeOperation = 'destination-atop'; - context.drawImage( - texture.baseTexture.getDrawableSource(), - crop.x, - crop.y, - crop.width, - crop.height, - 0, - 0, - crop.width, - crop.height - ); - - // context.globalCompositeOperation = 'copy'; - context.restore(); - }, - - /** - * Tint a texture pixel per pixel. - * - * @memberof PIXI.CanvasTinter - * @param {PIXI.Texture} texture - the texture to tint - * @param {number} color - the color to use to tint the sprite with - * @param {HTMLCanvasElement} canvas - the current canvas - */ - tintWithPerPixel: (texture, color, canvas) => - { - const context = canvas.getContext('2d'); - const crop = texture._frame.clone(); - const resolution = texture.baseTexture.resolution; - - crop.x *= resolution; - crop.y *= resolution; - crop.width *= resolution; - crop.height *= resolution; - - canvas.width = Math.ceil(crop.width); - canvas.height = Math.ceil(crop.height); - - context.save(); - context.globalCompositeOperation = 'copy'; - context.drawImage( - texture.baseTexture.getDrawableSource(), - crop.x, - crop.y, - crop.width, - crop.height, - 0, - 0, - crop.width, - crop.height - ); - context.restore(); - - const rgbValues = hex2rgb(color); - const r = rgbValues[0]; - const g = rgbValues[1]; - const b = rgbValues[2]; - - const pixelData = context.getImageData(0, 0, crop.width, crop.height); - - const pixels = pixelData.data; - - for (let i = 0; i < pixels.length; i += 4) - { - pixels[i + 0] *= r; - pixels[i + 1] *= g; - pixels[i + 2] *= b; - } - - context.putImageData(pixelData, 0, 0); - }, - - /** - * Rounds the specified color according to the CanvasTinter.cacheStepsPerColorChannel. - * - * @memberof PIXI.CanvasTinter - * @param {number} color - the color to round, should be a hex color - * @return {number} The rounded color. - */ - roundColor: (color) => - { - const step = CanvasTinter.cacheStepsPerColorChannel; - - const rgbValues = hex2rgb(color); - - rgbValues[0] = Math.min(255, (rgbValues[0] / step) * step); - rgbValues[1] = Math.min(255, (rgbValues[1] / step) * step); - rgbValues[2] = Math.min(255, (rgbValues[2] / step) * step); - - return rgb2hex(rgbValues); - }, - - /** - * Number of steps which will be used as a cap when rounding colors. - * - * @memberof PIXI.CanvasTinter - * @type {number} - */ - cacheStepsPerColorChannel: 8, - - /** - * Tint cache boolean flag. - * - * @memberof PIXI.CanvasTinter - * @type {boolean} - */ - convertTintToImage: false, - - /** - * Whether or not the Canvas BlendModes are supported, consequently the ability to tint using the multiply method. - * - * @memberof PIXI.CanvasTinter - * @type {boolean} - */ - canUseMultiply: canUseNewCanvasBlendModes(), - - /** - * The tinting method that will be used. - * - * @memberof PIXI.CanvasTinter - * @type {Function} - */ - tintMethod: () => - { // jslint-disable no-empty-function - - }, -}; - -CanvasTinter.tintMethod = CanvasTinter.canUseMultiply ? CanvasTinter.tintWithMultiply : CanvasTinter.tintWithPerPixel; diff --git a/packages/canvas/canvas-renderer/src/canvasUtils.js b/packages/canvas/canvas-renderer/src/canvasUtils.js new file mode 100644 index 0000000..62e16d4 --- /dev/null +++ b/packages/canvas/canvas-renderer/src/canvasUtils.js @@ -0,0 +1,288 @@ +import { hex2rgb, rgb2hex } from '@pixi/utils'; +import { canUseNewCanvasBlendModes } from './utils/canUseNewCanvasBlendModes'; + +/** + * Utility methods for Sprite/Texture tinting. + * + * Tinting with the CanvasRenderer involves creating a new canvas to use as a texture, + * so be aware of the performance implications. + * + * @namespace PIXI.canvasUtils + * @memberof PIXI + */ +export const canvasUtils = { + /** + * Basically this method just needs a sprite and a color and tints the sprite with the given color. + * + * @memberof PIXI.canvasUtils + * @param {PIXI.Sprite} sprite - the sprite to tint + * @param {number} color - the color to use to tint the sprite with + * @return {HTMLCanvasElement} The tinted canvas + */ + getTintedCanvas: (sprite, color) => + { + const texture = sprite.texture; + + color = canvasUtils.roundColor(color); + + const stringColor = `#${(`00000${(color | 0).toString(16)}`).substr(-6)}`; + + texture.tintCache = texture.tintCache || {}; + + const cachedCanvas = texture.tintCache[stringColor]; + + let canvas; + + if (cachedCanvas) + { + if (cachedCanvas.tintId === texture._updateID) + { + return texture.tintCache[stringColor]; + } + + canvas = texture.tintCache[stringColor]; + } + else + { + canvas = canvasUtils.canvas || document.createElement('canvas'); + } + + canvasUtils.tintMethod(texture, color, canvas); + + canvas.tintId = texture._updateID; + + if (canvasUtils.convertTintToImage) + { + // is this better? + const tintImage = new Image(); + + tintImage.src = canvas.toDataURL(); + + texture.tintCache[stringColor] = tintImage; + } + else + { + texture.tintCache[stringColor] = canvas; + // if we are not converting the texture to an image then we need to lose the reference to the canvas + canvasUtils.canvas = null; + } + + return canvas; + }, + + /** + * Tint a texture using the 'multiply' operation. + * + * @memberof PIXI.canvasUtils + * @param {PIXI.Texture} texture - the texture to tint + * @param {number} color - the color to use to tint the sprite with + * @param {HTMLCanvasElement} canvas - the current canvas + */ + tintWithMultiply: (texture, color, canvas) => + { + const context = canvas.getContext('2d'); + const crop = texture._frame.clone(); + const resolution = texture.baseTexture.resolution; + + crop.x *= resolution; + crop.y *= resolution; + crop.width *= resolution; + crop.height *= resolution; + + canvas.width = Math.ceil(crop.width); + canvas.height = Math.ceil(crop.height); + + context.save(); + context.fillStyle = `#${(`00000${(color | 0).toString(16)}`).substr(-6)}`; + + context.fillRect(0, 0, crop.width, crop.height); + + context.globalCompositeOperation = 'multiply'; + + const source = texture.baseTexture.getDrawableSource(); + + context.drawImage( + source, + crop.x, + crop.y, + crop.width, + crop.height, + 0, + 0, + crop.width, + crop.height + ); + + context.globalCompositeOperation = 'destination-atop'; + + context.drawImage( + source, + crop.x, + crop.y, + crop.width, + crop.height, + 0, + 0, + crop.width, + crop.height + ); + context.restore(); + }, + + /** + * Tint a texture using the 'overlay' operation. + * + * @memberof PIXI.canvasUtils + * @param {PIXI.Texture} texture - the texture to tint + * @param {number} color - the color to use to tint the sprite with + * @param {HTMLCanvasElement} canvas - the current canvas + */ + tintWithOverlay(texture, color, canvas) + { + const context = canvas.getContext('2d'); + const crop = texture._frame.clone(); + const resolution = texture.baseTexture.resolution; + + crop.x *= resolution; + crop.y *= resolution; + crop.width *= resolution; + crop.height *= resolution; + + canvas.width = Math.ceil(crop.width); + canvas.height = Math.ceil(crop.height); + + context.save(); + context.globalCompositeOperation = 'copy'; + context.fillStyle = `#${(`00000${(color | 0).toString(16)}`).substr(-6)}`; + context.fillRect(0, 0, crop.width, crop.height); + + context.globalCompositeOperation = 'destination-atop'; + context.drawImage( + texture.baseTexture.getDrawableSource(), + crop.x, + crop.y, + crop.width, + crop.height, + 0, + 0, + crop.width, + crop.height + ); + + // context.globalCompositeOperation = 'copy'; + context.restore(); + }, + + /** + * Tint a texture pixel per pixel. + * + * @memberof PIXI.canvasUtils + * @param {PIXI.Texture} texture - the texture to tint + * @param {number} color - the color to use to tint the sprite with + * @param {HTMLCanvasElement} canvas - the current canvas + */ + tintWithPerPixel: (texture, color, canvas) => + { + const context = canvas.getContext('2d'); + const crop = texture._frame.clone(); + const resolution = texture.baseTexture.resolution; + + crop.x *= resolution; + crop.y *= resolution; + crop.width *= resolution; + crop.height *= resolution; + + canvas.width = Math.ceil(crop.width); + canvas.height = Math.ceil(crop.height); + + context.save(); + context.globalCompositeOperation = 'copy'; + context.drawImage( + texture.baseTexture.getDrawableSource(), + crop.x, + crop.y, + crop.width, + crop.height, + 0, + 0, + crop.width, + crop.height + ); + context.restore(); + + const rgbValues = hex2rgb(color); + const r = rgbValues[0]; + const g = rgbValues[1]; + const b = rgbValues[2]; + + const pixelData = context.getImageData(0, 0, crop.width, crop.height); + + const pixels = pixelData.data; + + for (let i = 0; i < pixels.length; i += 4) + { + pixels[i + 0] *= r; + pixels[i + 1] *= g; + pixels[i + 2] *= b; + } + + context.putImageData(pixelData, 0, 0); + }, + + /** + * Rounds the specified color according to the canvasUtils.cacheStepsPerColorChannel. + * + * @memberof PIXI.canvasUtils + * @param {number} color - the color to round, should be a hex color + * @return {number} The rounded color. + */ + roundColor: (color) => + { + const step = canvasUtils.cacheStepsPerColorChannel; + + const rgbValues = hex2rgb(color); + + rgbValues[0] = Math.min(255, (rgbValues[0] / step) * step); + rgbValues[1] = Math.min(255, (rgbValues[1] / step) * step); + rgbValues[2] = Math.min(255, (rgbValues[2] / step) * step); + + return rgb2hex(rgbValues); + }, + + /** + * Number of steps which will be used as a cap when rounding colors. + * + * @memberof PIXI.canvasUtils + * @type {number} + */ + cacheStepsPerColorChannel: 8, + + /** + * Tint cache boolean flag. + * + * @memberof PIXI.canvasUtils + * @type {boolean} + */ + convertTintToImage: false, + + /** + * Whether or not the Canvas BlendModes are supported, consequently the ability to tint using the multiply method. + * + * @memberof PIXI.canvasUtils + * @type {boolean} + */ + canUseMultiply: canUseNewCanvasBlendModes(), + + /** + * The tinting method that will be used. + * + * @memberof PIXI.canvasUtils + * @type {Function} + */ + tintMethod: () => + { // jslint-disable no-empty-function + + }, +}; + +canvasUtils.tintMethod = canvasUtils.canUseMultiply ? canvasUtils.tintWithMultiply : canvasUtils.tintWithPerPixel; diff --git a/packages/canvas/canvas-renderer/src/index.js b/packages/canvas/canvas-renderer/src/index.js index 80987c3..71bc5ee 100644 --- a/packages/canvas/canvas-renderer/src/index.js +++ b/packages/canvas/canvas-renderer/src/index.js @@ -1,6 +1,6 @@ export * from './CanvasRenderer'; export * from './utils/canUseNewCanvasBlendModes'; -export * from './CanvasTinter'; +export * from './canvasUtils'; import './Renderer'; import './BaseTexture'; diff --git a/packages/canvas/canvas-sprite-tiling/src/TilingSprite.js b/packages/canvas/canvas-sprite-tiling/src/TilingSprite.js index 4eac1d7..fd8857f 100644 --- a/packages/canvas/canvas-sprite-tiling/src/TilingSprite.js +++ b/packages/canvas/canvas-sprite-tiling/src/TilingSprite.js @@ -1,5 +1,5 @@ import { TilingSprite } from '@pixi/sprite-tiling'; -import { CanvasTinter } from '@pixi/canvas-renderer'; +import { canvasUtils } from '@pixi/canvas-renderer'; import { CanvasRenderTarget } from '@pixi/utils'; /** @@ -40,7 +40,7 @@ // Tint the tiling sprite if (this.tint !== 0xFFFFFF) { - this._tintedCanvas = CanvasTinter.getTintedCanvas(this, this.tint); + this._tintedCanvas = canvasUtils.getTintedCanvas(this, this.tint); tempCanvas.context.drawImage(this._tintedCanvas, 0, 0); } else diff --git a/packages/canvas/canvas-sprite/src/CanvasSpriteRenderer.js b/packages/canvas/canvas-sprite/src/CanvasSpriteRenderer.js index 507571c..3cdea2a 100644 --- a/packages/canvas/canvas-sprite/src/CanvasSpriteRenderer.js +++ b/packages/canvas/canvas-sprite/src/CanvasSpriteRenderer.js @@ -1,6 +1,6 @@ import { SCALE_MODES, BLEND_MODES } from '@pixi/constants'; -import { Matrix, GroupD8 } from '@pixi/math'; -import { CanvasTinter } from '@pixi/canvas-renderer'; +import { Matrix, groupD8 } from '@pixi/math'; +import { canvasUtils } from '@pixi/canvas-renderer'; const canvasRenderWorldTransform = new Matrix(); @@ -96,7 +96,7 @@ { wt.copyTo(canvasRenderWorldTransform); wt = canvasRenderWorldTransform; - GroupD8.matrixAppendRotationInv(wt, texture.rotate, dx, dy); + groupD8.matrixAppendRotationInv(wt, texture.rotate, dx, dy); // the anchor has already been applied above, so lets set it to zero dx = 0; dy = 0; @@ -155,7 +155,7 @@ sprite._cachedTint = sprite.tint; // TODO clean up caching - how to clean up the caches? - sprite._tintedCanvas = CanvasTinter.getTintedCanvas(sprite, sprite.tint); + sprite._tintedCanvas = canvasUtils.getTintedCanvas(sprite, sprite.tint); } context.drawImage( diff --git a/packages/core/src/textures/Texture.js b/packages/core/src/textures/Texture.js index ea5c843..107b61e 100644 --- a/packages/core/src/textures/Texture.js +++ b/packages/core/src/textures/Texture.js @@ -45,7 +45,7 @@ * @param {PIXI.Rectangle} [frame] - The rectangle frame of the texture to show * @param {PIXI.Rectangle} [orig] - The area of original texture * @param {PIXI.Rectangle} [trim] - Trimmed rectangle of original texture - * @param {number} [rotate] - indicates how the texture was rotated by texture packer. See {@link PIXI.GroupD8} + * @param {number} [rotate] - indicates how the texture was rotated by texture packer. See {@link PIXI.groupD8} * @param {PIXI.Point} [anchor] - Default anchor point used for sprite placement / rotation */ constructor(baseTexture, frame, orig, trim, rotate, anchor) @@ -563,7 +563,7 @@ * set to 2 to compensate for texture packer rotation * set to 6 to compensate for spine packer rotation * can be used to rotate or mirror sprites - * See {@link PIXI.GroupD8} for explanation + * See {@link PIXI.groupD8} for explanation * * @member {number} */ diff --git a/packages/core/src/textures/TextureUvs.js b/packages/core/src/textures/TextureUvs.js index 322e973..735d928 100644 --- a/packages/core/src/textures/TextureUvs.js +++ b/packages/core/src/textures/TextureUvs.js @@ -1,4 +1,4 @@ -import { GroupD8 } from '@pixi/math'; +import { groupD8 } from '@pixi/math'; /** * Stores a texture's frame in UV coordinates, in @@ -85,7 +85,7 @@ * @protected * @param {PIXI.Rectangle} frame - The frame of the texture * @param {PIXI.Rectangle} baseFrame - The base frame of the texture - * @param {number} rotate - Rotation of frame, see {@link PIXI.GroupD8} + * @param {number} rotate - Rotation of frame, see {@link PIXI.groupD8} */ set(frame, baseFrame, rotate) { @@ -102,21 +102,21 @@ const cX = (frame.x / tw) + w2; const cY = (frame.y / th) + h2; - rotate = GroupD8.add(rotate, GroupD8.NW); // NW is top-left corner - this.x0 = cX + (w2 * GroupD8.uX(rotate)); - this.y0 = cY + (h2 * GroupD8.uY(rotate)); + rotate = groupD8.add(rotate, groupD8.NW); // NW is top-left corner + this.x0 = cX + (w2 * groupD8.uX(rotate)); + this.y0 = cY + (h2 * groupD8.uY(rotate)); - rotate = GroupD8.add(rotate, 2); // rotate 90 degrees clockwise - this.x1 = cX + (w2 * GroupD8.uX(rotate)); - this.y1 = cY + (h2 * GroupD8.uY(rotate)); + rotate = groupD8.add(rotate, 2); // rotate 90 degrees clockwise + this.x1 = cX + (w2 * groupD8.uX(rotate)); + this.y1 = cY + (h2 * groupD8.uY(rotate)); - rotate = GroupD8.add(rotate, 2); - this.x2 = cX + (w2 * GroupD8.uX(rotate)); - this.y2 = cY + (h2 * GroupD8.uY(rotate)); + rotate = groupD8.add(rotate, 2); + this.x2 = cX + (w2 * groupD8.uX(rotate)); + this.y2 = cY + (h2 * groupD8.uY(rotate)); - rotate = GroupD8.add(rotate, 2); - this.x3 = cX + (w2 * GroupD8.uX(rotate)); - this.y3 = cY + (h2 * GroupD8.uY(rotate)); + rotate = groupD8.add(rotate, 2); + this.x3 = cX + (w2 * groupD8.uX(rotate)); + this.y3 = cY + (h2 * groupD8.uY(rotate)); } else { diff --git a/packages/math/src/GroupD8.js b/packages/math/src/GroupD8.js deleted file mode 100644 index 513cea8..0000000 --- a/packages/math/src/GroupD8.js +++ /dev/null @@ -1,403 +0,0 @@ -// Your friendly neighbour https://en.wikipedia.org/wiki/Dihedral_group -// -// This file implements the dihedral group of order 16, also called -// of degree 8. That's why its called GroupD8. - -import { Matrix } from './Matrix'; - -/* - * Transform matrix for operation n is: - * | ux | vx | - * | uy | vy | - */ - -const ux = [1, 1, 0, -1, -1, -1, 0, 1, 1, 1, 0, -1, -1, -1, 0, 1]; -const uy = [0, 1, 1, 1, 0, -1, -1, -1, 0, 1, 1, 1, 0, -1, -1, -1]; -const vx = [0, -1, -1, -1, 0, 1, 1, 1, 0, 1, 1, 1, 0, -1, -1, -1]; -const vy = [1, 1, 0, -1, -1, -1, 0, 1, -1, -1, 0, 1, 1, 1, 0, -1]; - -/** - * [Cayley Table]{@link https://en.wikipedia.org/wiki/Cayley_table} - * for the composition of each rotation in the dihederal group D8. - * - * @type number[][] - * @private - */ -const rotationCayley = []; - -/** - * Matrices for each `GD8Symmetry` rotation. - * - * @type Matrix[] - * @private - */ -const rotationMatrices = []; - -/* - * Alias for {@code Math.sign}. - */ -const signum = Math.sign; - -/* - * Initializes `rotationCayley` and `rotationMatrices`. It is called - * only once below. - */ -function init() -{ - for (let i = 0; i < 16; i++) - { - const row = []; - - rotationCayley.push(row); - - for (let j = 0; j < 16; j++) - { - /* Multiplies rotation matrices i and j. */ - const _ux = signum((ux[i] * ux[j]) + (vx[i] * uy[j])); - const _uy = signum((uy[i] * ux[j]) + (vy[i] * uy[j])); - const _vx = signum((ux[i] * vx[j]) + (vx[i] * vy[j])); - const _vy = signum((uy[i] * vx[j]) + (vy[i] * vy[j])); - - /* Finds rotation matrix matching the product and pushes it. */ - for (let k = 0; k < 16; k++) - { - if (ux[k] === _ux && uy[k] === _uy - && vx[k] === _vx && vy[k] === _vy) - { - row.push(k); - break; - } - } - } - } - - for (let i = 0; i < 16; i++) - { - const mat = new Matrix(); - - mat.set(ux[i], uy[i], vx[i], vy[i], 0, 0); - rotationMatrices.push(mat); - } -} - -init(); - -/** - * @memberof PIXI - * @typedef {number} GD8Symmetry - * @see PIXI.GroupD8 - */ - -/** - * Implements the dihedral group D8, which is similar to - * [group D4]{@link http://mathworld.wolfram.com/DihedralGroupD4.html}; - * D8 is the same but with diagonals, and it is used for texture - * rotations. - * - * The directions the U- and V- axes after rotation - * of an angle of `a: GD8Constant` are the vectors `(uX(a), uY(a))` - * and `(vX(a), vY(a))`. These aren't necessarily unit vectors. - * - * **Origin:**
- * This is the small part of gameofbombs.com portal system. It works. - * - * @see PIXI.GroupD8.E - * @see PIXI.GroupD8.SE - * @see PIXI.GroupD8.S - * @see PIXI.GroupD8.SW - * @see PIXI.GroupD8.W - * @see PIXI.GroupD8.NW - * @see PIXI.GroupD8.N - * @see PIXI.GroupD8.NE - * @author Ivan @ivanpopelyshev - * @namespace PIXI.GroupD8 - * @memberof PIXI - */ -export const GroupD8 = { - /** - * | Rotation | Direction | - * |----------|-----------| - * | 0° | East | - * - * @memberof PIXI.GroupD8 - * @constant {PIXI.GD8Symmetry} - */ - E: 0, - - /** - * | Rotation | Direction | - * |----------|-----------| - * | 45°↻ | Southeast | - * - * @memberof PIXI.GroupD8 - * @constant {PIXI.GD8Symmetry} - */ - SE: 1, - - /** - * | Rotation | Direction | - * |----------|-----------| - * | 90°↻ | South | - * - * @memberof PIXI.GroupD8 - * @constant {PIXI.GD8Symmetry} - */ - S: 2, - - /** - * | Rotation | Direction | - * |----------|-----------| - * | 135°↻ | Southwest | - * - * @memberof PIXI.GroupD8 - * @constant {PIXI.GD8Symmetry} - */ - SW: 3, - - /** - * | Rotation | Direction | - * |----------|-----------| - * | 180° | West | - * - * @memberof PIXI.GroupD8 - * @constant {PIXI.GD8Symmetry} - */ - W: 4, - - /** - * | Rotation | Direction | - * |-------------|--------------| - * | -135°/225°↻ | Northwest | - * - * @memberof PIXI.GroupD8 - * @constant {PIXI.GD8Symmetry} - */ - NW: 5, - - /** - * | Rotation | Direction | - * |-------------|--------------| - * | -90°/270°↻ | North | - * - * @memberof PIXI.GroupD8 - * @constant {PIXI.GD8Symmetry} - */ - N: 6, - - /** - * | Rotation | Direction | - * |-------------|--------------| - * | -45°/315°↻ | Northeast | - * - * @memberof PIXI.GroupD8 - * @constant {PIXI.GD8Symmetry} - */ - NE: 7, - - /** - * Reflection about Y-axis. - * - * @memberof PIXI.GroupD8 - * @constant {PIXI.GD8Symmetry} - */ - MIRROR_VERTICAL: 8, - - /** - * Reflection about the main diagonal. - * - * @memberof PIXI.GroupD8 - * @constant {PIXI.GD8Symmetry} - */ - MAIN_DIAGONAL: 10, - - /** - * Reflection about X-axis. - * - * @memberof PIXI.GroupD8 - * @constant {PIXI.GD8Symmetry} - */ - MIRROR_HORIZONTAL: 12, - - /** - * Reflection about reverse diagonal. - * - * @memberof PIXI.GroupD8 - * @constant {PIXI.GD8Symmetry} - */ - REVERSE_DIAGONAL: 14, - - /** - * @memberof PIXI.GroupD8 - * @param {PIXI.GD8Symmetry} ind - sprite rotation angle. - * @return {PIXI.GD8Symmetry} The X-component of the U-axis - * after rotating the axes. - */ - uX: (ind) => ux[ind], - - /** - * @memberof PIXI.GroupD8 - * @param {PIXI.GD8Symmetry} ind - sprite rotation angle. - * @return {PIXI.GD8Symmetry} The Y-component of the U-axis - * after rotating the axes. - */ - uY: (ind) => uy[ind], - - /** - * @memberof PIXI.GroupD8 - * @param {PIXI.GD8Symmetry} ind - sprite rotation angle. - * @return {PIXI.GD8Symmetry} The X-component of the V-axis - * after rotating the axes. - */ - vX: (ind) => vx[ind], - - /** - * @memberof PIXI.GroupD8 - * @param {PIXI.GD8Symmetry} ind - sprite rotation angle. - * @return {PIXI.GD8Symmetry} The Y-component of the V-axis - * after rotating the axes. - */ - vY: (ind) => vy[ind], - - /** - * @memberof PIXI.GroupD8 - * @param {PIXI.GD8Symmetry} rotation - symmetry whose opposite - * is needed. Only rotations have opposite symmetries while - * reflections don't. - * @return {PIXI.GD8Symmetry} The opposite symmetry of `rotation` - */ - inv: (rotation) => - { - if (rotation & 8)// true only if between 8 & 15 (reflections) - { - return rotation & 15;// or rotation % 16 - } - - return (-rotation) & 7;// or (8 - rotation) % 8 - }, - - /** - * Composes the two D8 operations. - * - * Taking `^` as reflection: - * - * | | E=0 | S=2 | W=4 | N=6 | E^=8 | S^=10 | W^=12 | N^=14 | - * |-------|-----|-----|-----|-----|------|-------|-------|-------| - * | E=0 | E | S | W | N | E^ | S^ | W^ | N^ | - * | S=2 | S | W | N | E | S^ | W^ | N^ | E^ | - * | W=4 | W | N | E | S | W^ | N^ | E^ | S^ | - * | N=6 | N | E | S | W | N^ | E^ | S^ | W^ | - * | E^=8 | E^ | N^ | W^ | S^ | E | N | W | S | - * | S^=10 | S^ | E^ | N^ | W^ | S | E | N | W | - * | W^=12 | W^ | S^ | E^ | N^ | W | S | E | N | - * | N^=14 | N^ | W^ | S^ | E^ | N | W | S | E | - * - * [This is a Cayley table]{@link https://en.wikipedia.org/wiki/Cayley_table} - * @memberof PIXI.GroupD8 - * @param {PIXI.GD8Symmetry} rotationSecond - Second operation, which - * is the row in the above cayley table. - * @param {PIXI.GD8Symmetry} rotationFirst - First operation, which - * is the column in the above cayley table. - * @return {PIXI.GD8Symmetry} Composed operation - */ - add: (rotationSecond, rotationFirst) => ( - rotationCayley[rotationSecond][rotationFirst] - ), - - /** - * Reverse of `add`. - * - * @memberof PIXI.GroupD8 - * @param {PIXI.GD8Symmetry} rotationSecond - Second operation - * @param {PIXI.GD8Symmetry} rotationFirst - First operation - * @return {PIXI.GD8Symmetry} Result - */ - sub: (rotationSecond, rotationFirst) => ( - rotationCayley[rotationSecond][GroupD8.inv(rotationFirst)] - ), - - /** - * Adds 180 degrees to rotation, which is a commutative - * operation. - * - * @memberof PIXI.GroupD8 - * @param {number} rotation - The number to rotate. - * @returns {number} Rotated number - */ - rotate180: (rotation) => rotation ^ 4, - - /** - * Checks if the rotation angle is vertical, i.e. south - * or north. It doesn't work for reflections. - * - * @memberof PIXI.GroupD8 - * @param {PIXI.GD8Symmetry} rotation - The number to check. - * @returns {boolean} Whether or not the direction is vertical - */ - isVertical: (rotation) => (rotation & 3) === 2, // rotation % 4 === 2 - - /** - * Approximates the vector `V(dx,dy)` into one of the - * eight directions provided by `GroupD8`. - * - * @memberof PIXI.GroupD8 - * @param {number} dx - X-component of the vector - * @param {number} dy - Y-component of the vector - * @return {PIXI.GD8Symmetry} Approximation of the vector into - * one of the eight symmetries. - */ - byDirection: (dx, dy) => - { - if (Math.abs(dx) * 2 <= Math.abs(dy)) - { - if (dy >= 0) - { - return GroupD8.S; - } - - return GroupD8.N; - } - else if (Math.abs(dy) * 2 <= Math.abs(dx)) - { - if (dx > 0) - { - return GroupD8.E; - } - - return GroupD8.W; - } - else if (dy > 0) - { - if (dx > 0) - { - return GroupD8.SE; - } - - return GroupD8.SW; - } - else if (dx > 0) - { - return GroupD8.NE; - } - - return GroupD8.NW; - }, - - /** - * Helps sprite to compensate texture packer rotation. - * - * @memberof PIXI.GroupD8 - * @param {PIXI.Matrix} matrix - sprite world matrix - * @param {PIXI.GD8Symmetry} rotation - The rotation factor to use. - * @param {number} tx - sprite anchoring - * @param {number} ty - sprite anchoring - */ - matrixAppendRotationInv: (matrix, rotation, tx = 0, ty = 0) => - { - // Packer used "rotation", we use "inv(rotation)" - const mat = rotationMatrices[GroupD8.inv(rotation)]; - - mat.tx = tx; - mat.ty = ty; - matrix.append(mat); - }, -}; diff --git a/packages/math/src/groupD8.js b/packages/math/src/groupD8.js new file mode 100644 index 0000000..8b9f542 --- /dev/null +++ b/packages/math/src/groupD8.js @@ -0,0 +1,403 @@ +// Your friendly neighbour https://en.wikipedia.org/wiki/Dihedral_group +// +// This file implements the dihedral group of order 16, also called +// of degree 8. That's why its called groupD8. + +import { Matrix } from './Matrix'; + +/* + * Transform matrix for operation n is: + * | ux | vx | + * | uy | vy | + */ + +const ux = [1, 1, 0, -1, -1, -1, 0, 1, 1, 1, 0, -1, -1, -1, 0, 1]; +const uy = [0, 1, 1, 1, 0, -1, -1, -1, 0, 1, 1, 1, 0, -1, -1, -1]; +const vx = [0, -1, -1, -1, 0, 1, 1, 1, 0, 1, 1, 1, 0, -1, -1, -1]; +const vy = [1, 1, 0, -1, -1, -1, 0, 1, -1, -1, 0, 1, 1, 1, 0, -1]; + +/** + * [Cayley Table]{@link https://en.wikipedia.org/wiki/Cayley_table} + * for the composition of each rotation in the dihederal group D8. + * + * @type number[][] + * @private + */ +const rotationCayley = []; + +/** + * Matrices for each `GD8Symmetry` rotation. + * + * @type Matrix[] + * @private + */ +const rotationMatrices = []; + +/* + * Alias for {@code Math.sign}. + */ +const signum = Math.sign; + +/* + * Initializes `rotationCayley` and `rotationMatrices`. It is called + * only once below. + */ +function init() +{ + for (let i = 0; i < 16; i++) + { + const row = []; + + rotationCayley.push(row); + + for (let j = 0; j < 16; j++) + { + /* Multiplies rotation matrices i and j. */ + const _ux = signum((ux[i] * ux[j]) + (vx[i] * uy[j])); + const _uy = signum((uy[i] * ux[j]) + (vy[i] * uy[j])); + const _vx = signum((ux[i] * vx[j]) + (vx[i] * vy[j])); + const _vy = signum((uy[i] * vx[j]) + (vy[i] * vy[j])); + + /* Finds rotation matrix matching the product and pushes it. */ + for (let k = 0; k < 16; k++) + { + if (ux[k] === _ux && uy[k] === _uy + && vx[k] === _vx && vy[k] === _vy) + { + row.push(k); + break; + } + } + } + } + + for (let i = 0; i < 16; i++) + { + const mat = new Matrix(); + + mat.set(ux[i], uy[i], vx[i], vy[i], 0, 0); + rotationMatrices.push(mat); + } +} + +init(); + +/** + * @memberof PIXI + * @typedef {number} GD8Symmetry + * @see PIXI.groupD8 + */ + +/** + * Implements the dihedral group D8, which is similar to + * [group D4]{@link http://mathworld.wolfram.com/DihedralGroupD4.html}; + * D8 is the same but with diagonals, and it is used for texture + * rotations. + * + * The directions the U- and V- axes after rotation + * of an angle of `a: GD8Constant` are the vectors `(uX(a), uY(a))` + * and `(vX(a), vY(a))`. These aren't necessarily unit vectors. + * + * **Origin:**
+ * This is the small part of gameofbombs.com portal system. It works. + * + * @see PIXI.groupD8.E + * @see PIXI.groupD8.SE + * @see PIXI.groupD8.S + * @see PIXI.groupD8.SW + * @see PIXI.groupD8.W + * @see PIXI.groupD8.NW + * @see PIXI.groupD8.N + * @see PIXI.groupD8.NE + * @author Ivan @ivanpopelyshev + * @namespace PIXI.groupD8 + * @memberof PIXI + */ +export const groupD8 = { + /** + * | Rotation | Direction | + * |----------|-----------| + * | 0° | East | + * + * @memberof PIXI.groupD8 + * @constant {PIXI.GD8Symmetry} + */ + E: 0, + + /** + * | Rotation | Direction | + * |----------|-----------| + * | 45°↻ | Southeast | + * + * @memberof PIXI.groupD8 + * @constant {PIXI.GD8Symmetry} + */ + SE: 1, + + /** + * | Rotation | Direction | + * |----------|-----------| + * | 90°↻ | South | + * + * @memberof PIXI.groupD8 + * @constant {PIXI.GD8Symmetry} + */ + S: 2, + + /** + * | Rotation | Direction | + * |----------|-----------| + * | 135°↻ | Southwest | + * + * @memberof PIXI.groupD8 + * @constant {PIXI.GD8Symmetry} + */ + SW: 3, + + /** + * | Rotation | Direction | + * |----------|-----------| + * | 180° | West | + * + * @memberof PIXI.groupD8 + * @constant {PIXI.GD8Symmetry} + */ + W: 4, + + /** + * | Rotation | Direction | + * |-------------|--------------| + * | -135°/225°↻ | Northwest | + * + * @memberof PIXI.groupD8 + * @constant {PIXI.GD8Symmetry} + */ + NW: 5, + + /** + * | Rotation | Direction | + * |-------------|--------------| + * | -90°/270°↻ | North | + * + * @memberof PIXI.groupD8 + * @constant {PIXI.GD8Symmetry} + */ + N: 6, + + /** + * | Rotation | Direction | + * |-------------|--------------| + * | -45°/315°↻ | Northeast | + * + * @memberof PIXI.groupD8 + * @constant {PIXI.GD8Symmetry} + */ + NE: 7, + + /** + * Reflection about Y-axis. + * + * @memberof PIXI.groupD8 + * @constant {PIXI.GD8Symmetry} + */ + MIRROR_VERTICAL: 8, + + /** + * Reflection about the main diagonal. + * + * @memberof PIXI.groupD8 + * @constant {PIXI.GD8Symmetry} + */ + MAIN_DIAGONAL: 10, + + /** + * Reflection about X-axis. + * + * @memberof PIXI.groupD8 + * @constant {PIXI.GD8Symmetry} + */ + MIRROR_HORIZONTAL: 12, + + /** + * Reflection about reverse diagonal. + * + * @memberof PIXI.groupD8 + * @constant {PIXI.GD8Symmetry} + */ + REVERSE_DIAGONAL: 14, + + /** + * @memberof PIXI.groupD8 + * @param {PIXI.GD8Symmetry} ind - sprite rotation angle. + * @return {PIXI.GD8Symmetry} The X-component of the U-axis + * after rotating the axes. + */ + uX: (ind) => ux[ind], + + /** + * @memberof PIXI.groupD8 + * @param {PIXI.GD8Symmetry} ind - sprite rotation angle. + * @return {PIXI.GD8Symmetry} The Y-component of the U-axis + * after rotating the axes. + */ + uY: (ind) => uy[ind], + + /** + * @memberof PIXI.groupD8 + * @param {PIXI.GD8Symmetry} ind - sprite rotation angle. + * @return {PIXI.GD8Symmetry} The X-component of the V-axis + * after rotating the axes. + */ + vX: (ind) => vx[ind], + + /** + * @memberof PIXI.groupD8 + * @param {PIXI.GD8Symmetry} ind - sprite rotation angle. + * @return {PIXI.GD8Symmetry} The Y-component of the V-axis + * after rotating the axes. + */ + vY: (ind) => vy[ind], + + /** + * @memberof PIXI.groupD8 + * @param {PIXI.GD8Symmetry} rotation - symmetry whose opposite + * is needed. Only rotations have opposite symmetries while + * reflections don't. + * @return {PIXI.GD8Symmetry} The opposite symmetry of `rotation` + */ + inv: (rotation) => + { + if (rotation & 8)// true only if between 8 & 15 (reflections) + { + return rotation & 15;// or rotation % 16 + } + + return (-rotation) & 7;// or (8 - rotation) % 8 + }, + + /** + * Composes the two D8 operations. + * + * Taking `^` as reflection: + * + * | | E=0 | S=2 | W=4 | N=6 | E^=8 | S^=10 | W^=12 | N^=14 | + * |-------|-----|-----|-----|-----|------|-------|-------|-------| + * | E=0 | E | S | W | N | E^ | S^ | W^ | N^ | + * | S=2 | S | W | N | E | S^ | W^ | N^ | E^ | + * | W=4 | W | N | E | S | W^ | N^ | E^ | S^ | + * | N=6 | N | E | S | W | N^ | E^ | S^ | W^ | + * | E^=8 | E^ | N^ | W^ | S^ | E | N | W | S | + * | S^=10 | S^ | E^ | N^ | W^ | S | E | N | W | + * | W^=12 | W^ | S^ | E^ | N^ | W | S | E | N | + * | N^=14 | N^ | W^ | S^ | E^ | N | W | S | E | + * + * [This is a Cayley table]{@link https://en.wikipedia.org/wiki/Cayley_table} + * @memberof PIXI.groupD8 + * @param {PIXI.GD8Symmetry} rotationSecond - Second operation, which + * is the row in the above cayley table. + * @param {PIXI.GD8Symmetry} rotationFirst - First operation, which + * is the column in the above cayley table. + * @return {PIXI.GD8Symmetry} Composed operation + */ + add: (rotationSecond, rotationFirst) => ( + rotationCayley[rotationSecond][rotationFirst] + ), + + /** + * Reverse of `add`. + * + * @memberof PIXI.groupD8 + * @param {PIXI.GD8Symmetry} rotationSecond - Second operation + * @param {PIXI.GD8Symmetry} rotationFirst - First operation + * @return {PIXI.GD8Symmetry} Result + */ + sub: (rotationSecond, rotationFirst) => ( + rotationCayley[rotationSecond][groupD8.inv(rotationFirst)] + ), + + /** + * Adds 180 degrees to rotation, which is a commutative + * operation. + * + * @memberof PIXI.groupD8 + * @param {number} rotation - The number to rotate. + * @returns {number} Rotated number + */ + rotate180: (rotation) => rotation ^ 4, + + /** + * Checks if the rotation angle is vertical, i.e. south + * or north. It doesn't work for reflections. + * + * @memberof PIXI.groupD8 + * @param {PIXI.GD8Symmetry} rotation - The number to check. + * @returns {boolean} Whether or not the direction is vertical + */ + isVertical: (rotation) => (rotation & 3) === 2, // rotation % 4 === 2 + + /** + * Approximates the vector `V(dx,dy)` into one of the + * eight directions provided by `groupD8`. + * + * @memberof PIXI.groupD8 + * @param {number} dx - X-component of the vector + * @param {number} dy - Y-component of the vector + * @return {PIXI.GD8Symmetry} Approximation of the vector into + * one of the eight symmetries. + */ + byDirection: (dx, dy) => + { + if (Math.abs(dx) * 2 <= Math.abs(dy)) + { + if (dy >= 0) + { + return groupD8.S; + } + + return groupD8.N; + } + else if (Math.abs(dy) * 2 <= Math.abs(dx)) + { + if (dx > 0) + { + return groupD8.E; + } + + return groupD8.W; + } + else if (dy > 0) + { + if (dx > 0) + { + return groupD8.SE; + } + + return groupD8.SW; + } + else if (dx > 0) + { + return groupD8.NE; + } + + return groupD8.NW; + }, + + /** + * Helps sprite to compensate texture packer rotation. + * + * @memberof PIXI.groupD8 + * @param {PIXI.Matrix} matrix - sprite world matrix + * @param {PIXI.GD8Symmetry} rotation - The rotation factor to use. + * @param {number} tx - sprite anchoring + * @param {number} ty - sprite anchoring + */ + matrixAppendRotationInv: (matrix, rotation, tx = 0, ty = 0) => + { + // Packer used "rotation", we use "inv(rotation)" + const mat = rotationMatrices[groupD8.inv(rotation)]; + + mat.tx = tx; + mat.ty = ty; + matrix.append(mat); + }, +}; diff --git a/packages/math/src/index.js b/packages/math/src/index.js index 4ff1eef..a500742 100644 --- a/packages/math/src/index.js +++ b/packages/math/src/index.js @@ -6,7 +6,7 @@ export * from './Point'; export * from './ObservablePoint'; export * from './Matrix'; -export * from './GroupD8'; +export * from './groupD8'; export * from './Transform'; export * from './shapes/Circle'; export * from './shapes/Ellipse';