UNPKG

phaser

Version:

A fast, free and fun HTML5 Game Framework for Desktop and Mobile web browsers from the team at Phaser Studio Inc.

502 lines (455 loc) 17.1 kB
/** * @author Richard Davey <rich@phaser.io> * @copyright 2013-2026 Phaser Studio Inc. * @license {@link https://opensource.org/licenses/MIT|MIT License} */ var CanvasPool = require('../display/canvas/CanvasPool'); var Class = require('../utils/Class'); var IsSizePowerOfTwo = require('../math/pow2/IsSizePowerOfTwo'); var ScaleModes = require('../renderer/ScaleModes'); var WebGLTextureWrapper = require('../renderer/webgl/wrappers/WebGLTextureWrapper'); /** * @classdesc * A Texture Source is the encapsulation of the actual source data for a Texture. * * It wraps the raw pixel data — typically an Image Element loaded from the file system or * network, a Canvas Element, or a Video Element — and manages the corresponding WebGL * texture object used by the renderer. In WebGL mode, the TextureSource creates and owns * the `WebGLTextureWrapper` that is uploaded to the GPU. * * You will not usually create a TextureSource directly. Instead, it is created automatically * when a Texture is added to the Texture Manager. You can access it via `Texture.source`, * which is an array of TextureSource instances. Most textures have a single source, but * multi-atlas textures can have several. * * A Texture can contain multiple Texture Sources, which only happens when a multi-atlas is loaded. * * @class TextureSource * @memberof Phaser.Textures * @constructor * @since 3.0.0 * * @param {Phaser.Textures.Texture} texture - The Texture this TextureSource belongs to. * @param {Phaser.Types.Textures.TextureSource} source - The source image data. * @param {number} [width] - Optional width of the source image. If not given it's derived from the source itself. * @param {number} [height] - Optional height of the source image. If not given it's derived from the source itself. * @param {boolean} [flipY=true] - Sets the `UNPACK_FLIP_Y_WEBGL` flag the WebGL Texture uses during upload. */ var TextureSource = new Class({ initialize: function TextureSource (texture, source, width, height, flipY) { if (flipY === undefined) { flipY = true; } var game = texture.manager.game; /** * A reference to the Canvas or WebGL Renderer. * * @name Phaser.Textures.TextureSource#renderer * @type {(Phaser.Renderer.Canvas.CanvasRenderer|Phaser.Renderer.WebGL.WebGLRenderer)} * @since 3.7.0 */ this.renderer = game.renderer; /** * The Texture this TextureSource instance belongs to. * * @name Phaser.Textures.TextureSource#texture * @type {Phaser.Textures.Texture} * @since 3.0.0 */ this.texture = texture; /** * The source of the image data. * * This is either an Image Element, a Canvas Element, a Video Element, a RenderTexture or a WebGLTextureWrapper. * * In Phaser 3.60 and above it can also be a Compressed Texture data object. * * @name Phaser.Textures.TextureSource#source * @type {Phaser.Types.Textures.TextureSource} * @since 3.12.0 */ this.source = source; /** * The image data. * * This is either an Image element, Canvas element, Video Element, * or null if the `source` is not an HTMLElement. * Non-HTMLElement sources include compressed textures, data arrays, * and references to other WebGL textures. * * @name Phaser.Textures.TextureSource#image * @type {Phaser.Types.Textures.TextureSourceElement | null} * @since 3.0.0 */ this.image = (source.compressed) ? null : source; /** * Holds the compressed texture algorithm, or `null` if it's not a compressed texture. * * Prior to Phaser 3.60 this value always held `null`. * * @name Phaser.Textures.TextureSource#compressionAlgorithm * @type {number} * @default null * @since 3.0.0 */ this.compressionAlgorithm = (source.compressed) ? source.format : null; /** * The resolution of the source image. * * @name Phaser.Textures.TextureSource#resolution * @type {number} * @default 1 * @since 3.0.0 */ this.resolution = 1; /** * The width of the source image. If not specified in the constructor it will check * the `naturalWidth`, `videoWidth`, and then `width` properties of the source image. * * @name Phaser.Textures.TextureSource#width * @type {number} * @since 3.0.0 */ this.width = width || source.naturalWidth || source.videoWidth || source.width || 0; /** * The height of the source image. If not specified in the constructor it will check * the `naturalHeight`, `videoHeight`, and then `height` properties of the source image. * * @name Phaser.Textures.TextureSource#height * @type {number} * @since 3.0.0 */ this.height = height || source.naturalHeight || source.videoHeight || source.height || 0; /** * The Scale Mode the image will use when rendering. * Either Linear or Nearest. * * @name Phaser.Textures.TextureSource#scaleMode * @type {number} * @since 3.0.0 */ this.scaleMode = ScaleModes.DEFAULT; /** * Is the source image a Canvas Element? * * @name Phaser.Textures.TextureSource#isCanvas * @type {boolean} * @since 3.0.0 */ this.isCanvas = (source instanceof HTMLCanvasElement); /** * Is the source image a Video Element? * * @name Phaser.Textures.TextureSource#isVideo * @type {boolean} * @since 3.20.0 */ this.isVideo = (window.hasOwnProperty('HTMLVideoElement') && source instanceof HTMLVideoElement); /** * Is the source image a Render Texture? * * @name Phaser.Textures.TextureSource#isRenderTexture * @type {boolean} * @since 3.12.0 */ this.isRenderTexture = (source.type === 'RenderTexture' || source.type === 'DynamicTexture'); /** * Is the source image a WebGLTextureWrapper? * * @name Phaser.Textures.TextureSource#isGLTexture * @type {boolean} * @since 3.19.0 */ this.isGLTexture = source instanceof WebGLTextureWrapper; /** * Are the source image dimensions a power of two? * * @name Phaser.Textures.TextureSource#isPowerOf2 * @type {boolean} * @since 3.0.0 */ this.isPowerOf2 = IsSizePowerOfTwo(this.width, this.height); /** * The wrapped WebGL Texture of the source image. * If this TextureSource is driven from a WebGLTexture already, * then this wrapper contains a reference to that WebGLTexture. * * @name Phaser.Textures.TextureSource#glTexture * @type {?Phaser.Renderer.WebGL.Wrappers.WebGLTextureWrapper} * @default null * @since 3.0.0 */ this.glTexture = null; /** * Whether the `UNPACK_FLIP_Y_WEBGL` flag is set on this WebGL Texture during upload. * When `true`, the texture data is flipped vertically as it is uploaded to the GPU. * * @name Phaser.Textures.TextureSource#flipY * @type {boolean} * @since 3.20.0 */ this.flipY = flipY; this.init(game); }, /** * Creates a WebGL Texture, if required, and sets the Texture filter mode. * * @method Phaser.Textures.TextureSource#init * @since 3.0.0 * * @param {Phaser.Game} game - A reference to the Phaser Game instance. */ init: function (game) { var renderer = this.renderer; if (renderer) { var source = this.source; if (renderer.gl) { var image = this.image; var flipY = this.flipY; var width = this.width; var height = this.height; var scaleMode = this.scaleMode; if (this.isCanvas) { this.glTexture = renderer.createCanvasTexture(image, false, flipY); } else if (this.isVideo) { this.glTexture = renderer.createVideoTexture(image, false, flipY); } else if (this.isRenderTexture) { this.glTexture = renderer.createTextureFromSource(null, width, height, scaleMode, undefined, flipY); } else if (this.isGLTexture) { this.glTexture = source; } else if (this.compressionAlgorithm) { this.glTexture = renderer.createTextureFromSource(source, undefined, undefined, scaleMode, undefined, flipY); } else if (source instanceof Uint8Array) { this.glTexture = renderer.createUint8ArrayTexture(source, width, height, undefined, flipY); } else { this.glTexture = renderer.createTextureFromSource(image, width, height, scaleMode, undefined, flipY); } if (typeof WEBGL_DEBUG) { this.glTexture.spectorMetadata = { textureKey: this.texture.key }; } } else if (this.isRenderTexture) { this.image = source.canvas; } } if (!game.config.antialias) { this.setFilter(1); } }, /** * Sets the Filter Mode for this Texture. * * The mode can be either Linear, the default, or Nearest. * * For pixel-art you should use Nearest. * * @method Phaser.Textures.TextureSource#setFilter * @since 3.0.0 * * @param {Phaser.Textures.FilterMode} filterMode - The Filter Mode. */ setFilter: function (filterMode) { if (this.renderer && this.renderer.gl) { this.renderer.setTextureFilter(this.glTexture, filterMode); } this.scaleMode = filterMode; }, /** * Sets the `UNPACK_FLIP_Y_WEBGL` flag for the WebGL Texture during texture upload. * * @method Phaser.Textures.TextureSource#setFlipY * @since 3.20.0 * * @param {boolean} [value=true] - Should the WebGL Texture be flipped on the Y axis on texture upload or not? */ setFlipY: function (value) { if (value === undefined) { value = true; } if (value === this.flipY) { return this; } this.flipY = value; this.update(); return this; }, /** * Sets the wrap mode for this TextureSource. * This is only available for WebGL. * * The wrap mode can be one of the following: * * - Phaser.Textures.WrapMode.CLAMP_TO_EDGE * - Phaser.Textures.WrapMode.REPEAT * - Phaser.Textures.WrapMode.MIRRORED_REPEAT * * Note that only CLAMP_TO_EDGE is supported for non-power of two textures. * If another wrap mode is specified for such a texture, it will be ignored. * * @method Phaser.Textures.TextureSource#setWrap * @since 4.0.0 * @webglonly * * @param {Phaser.Textures.WrapMode} wrapModeS - The wrap mode for the S (horizontal) axis. * @param {Phaser.Textures.WrapMode} wrapModeT - The wrap mode for the T (vertical) axis. * * @return {this} This TextureSource instance. */ setWrap: function (wrapModeS, wrapModeT) { if (this.renderer && this.renderer.gl) { this.renderer.setTextureWrap(this.glTexture, wrapModeS, wrapModeT); } return this; }, /** * Update the underlying WebGLTexture with the current source data. * This is called automatically by game systems which manage textures, * such as Text or Video, or on internal data changes. * You should not need to call this method manually unless you are * manually updating the source data. * * @method Phaser.Textures.TextureSource#update * @since 3.7.0 */ update: function () { var renderer = this.renderer; var image = this.image; var flipY = this.flipY; var gl = renderer.gl; if (gl) { var textureWrapper = this.glTexture; if (this.isCanvas) { renderer.updateCanvasTexture(image, textureWrapper, flipY); } else if (this.isVideo) { renderer.updateVideoTexture(image, textureWrapper, flipY); } else { textureWrapper.update( this.source, this.width, this.height, flipY, textureWrapper.wrapS, textureWrapper.wrapT, textureWrapper.magFilter, textureWrapper.minFilter, textureWrapper.format ); } } }, /** * Updates the dimensions of this Texture Source. * This is called automatically by game systems which manage textures, * such as Text, which renders to a dedicated canvas that changes size. * * @method Phaser.Textures.TextureSource#updateSize * @since 4.0.0 * @param {number} width - The new width of the source image. * @param {number} height - The new height of the source image. */ updateSize: function (width, height) { if (this.width === width && this.height === height) { return; } this.width = width; this.height = height; this.isPowerOf2 = IsSizePowerOfTwo(width, height); }, /** * Change the source data of this TextureSource. * This will update the underlying WebGLTexture with the new source data. * * Note that this will not update any consumers of the texture. * Frames and game objects with the `Size` component * will automatically cache width and height on creation, so you will need * to update them if you change the resolution of the texture this way. * * @example * // `logo` is a sprite * // `newImage` is an ImageElement with the new source data * logo.frame.source.updateSource(newImage); * logo.texture.get("__BASE").setSize(logo.frame.source.width, logo.frame.source.height); * logo.setSize(logo.frame.source.width, logo.frame.source.height); * logo.setOrigin(0.5, 0.5); * * @method Phaser.Textures.TextureSource#updateSource * @since 4.0.0 * * @param {Phaser.Types.Textures.TextureSource} source - The new source data. */ updateSource: function (source) { // Tidy up unused pool canvases. if (this.isCanvas && source !== this.image && !(source instanceof HTMLCanvasElement)) { CanvasPool.remove(this.image); } this.source = source; this.image = (source.compressed) ? null : source; this.compressionAlgorithm = (source.compressed) ? source.format : null; this.width = source.naturalWidth || source.videoWidth || source.width || 0; this.height = source.naturalHeight || source.videoHeight || source.height || 0; this.isPowerOf2 = IsSizePowerOfTwo(this.width, this.height); this.isCanvas = (source instanceof HTMLCanvasElement); this.isVideo = (window.hasOwnProperty('HTMLVideoElement') && source instanceof HTMLVideoElement); this.isRenderTexture = (source.type === 'RenderTexture' || source.type === 'DynamicTexture'); this.isGLTexture = source instanceof WebGLTextureWrapper; this.update(); }, /** * Destroys this Texture Source and frees associated resources. * * In WebGL mode, the underlying WebGL texture is deleted from the GPU via the renderer. * If the source is a canvas, it is returned to the CanvasPool. All internal references * are then set to `null`. * * @method Phaser.Textures.TextureSource#destroy * @since 3.0.0 */ destroy: function () { if (this.glTexture) { this.renderer.deleteTexture(this.glTexture); } if (this.isCanvas) { CanvasPool.remove(this.image); } this.renderer = null; this.texture = null; this.source = null; this.image = null; this.glTexture = null; } }); module.exports = TextureSource;