UNPKG

phaser

Version:

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

1,326 lines (1,182 loc) 49.9 kB
/** * @author Richard Davey <rich@phaser.io> * @copyright 2013-2026 Phaser Studio Inc. * @license {@link https://opensource.org/licenses/MIT|MIT License} */ var BlendModes = require('../renderer/BlendModes'); var Camera = require('../cameras/2d/Camera'); var CanvasPool = require('../display/canvas/CanvasPool'); var Class = require('../utils/Class'); var CONST = require('../const'); var DrawingContext = require('../renderer/webgl/DrawingContext'); var Frame = require('./Frame'); var GetFastValue = require('../utils/object/GetFastValue'); var Texture = require('./Texture'); var Utils = require('../renderer/webgl/Utils'); var DynamicTextureCommands = require('./DynamicTextureCommands'); var TransformMatrix = require('../gameobjects/components/TransformMatrix'); /** * @classdesc * A Dynamic Texture is a special texture that allows you to draw textures, frames and most kind of * Game Objects directly to it. * * You can take many complex objects and draw them to this one texture, which can then be used as the * base texture for other Game Objects, such as Sprites. Should you then update this texture, all * Game Objects using it will instantly be updated as well, reflecting the changes immediately. * * It's a powerful way to generate dynamic textures at run-time that are WebGL friendly and don't invoke * expensive GPU uploads on each change. * * ```js * const t = this.textures.addDynamicTexture('player', 64, 128); * // draw objects to t * this.add.sprite(x, y, 'player'); * this.render(); * ``` * * Because this is a standard Texture within Phaser, you can add frames to it, meaning you can use it * to generate sprite sheets, texture atlases or tile sets. * * Under WebGL1, a FrameBuffer, which is what this Dynamic Texture uses internally, cannot be anti-aliased. * This means that when drawing objects such as Shapes or Graphics instances to this texture, they may appear * to be drawn with no aliasing around the edges. This is a technical limitation of WebGL1. To get around it, * create your shape as a texture in an art package, then draw that to this texture. * * If you activate mipmap support in your game, it will not automatically * be applied to DynamicTextures. * This is because regenerating the mipmap for a texture * costs over 10 microseconds, a big performance loss for a single frame. * If you want to render your DynamicTextures with mipmaps, * you must also activate the render config option `mipmapRegeneration`. * * In the event that the WebGL context is lost, this DynamicTexture will * lose its contents. Once context is restored (signalled by the `restorewebgl` * event), you can choose to redraw the contents of the DynamicTexture. * You are responsible for the redrawing logic. * * @class DynamicTexture * @extends Phaser.Textures.Texture * @memberof Phaser.Textures * @constructor * @since 3.60.0 * * @param {Phaser.Textures.TextureManager} manager - A reference to the Texture Manager this Texture belongs to. * @param {string} key - The unique string-based key of this Texture. * @param {number} [width=256] - The width of this Dynamic Texture in pixels. Defaults to 256 x 256. * @param {number} [height=256] - The height of this Dynamic Texture in pixels. Defaults to 256 x 256. * @param {boolean} [forceEven=true] - Force the given width and height to be rounded to even values. This significantly improves the rendering quality. Set to false if you know you need an odd sized texture. */ var DynamicTexture = new Class({ Extends: Texture, initialize: function DynamicTexture (manager, key, width, height, forceEven) { if (width === undefined) { width = 256; } if (height === undefined) { height = 256; } if (forceEven === undefined) { forceEven = true; } /** * The internal data type of this object. * * @name Phaser.Textures.DynamicTexture#type * @type {string} * @readonly * @since 3.60.0 */ this.type = 'DynamicTexture'; var renderer = manager.game.renderer; var isCanvas = (renderer && renderer.type === CONST.CANVAS); var source = (isCanvas) ? CanvasPool.create2D(this, width, height) : [ this ]; Texture.call(this, manager, key, source, width, height); this.add('__BASE', 0, 0, 0, width, height); /** * A reference to either the Canvas or WebGL Renderer that the Game instance is using. * * @name Phaser.Textures.DynamicTexture#renderer * @type {(Phaser.Renderer.Canvas.CanvasRenderer|Phaser.Renderer.WebGL.WebGLRenderer)} * @since 3.2.0 */ this.renderer = renderer; /** * The width of this Dynamic Texture. * * Treat this property as read-only. Use the `setSize` method to change the size. * * @name Phaser.Textures.DynamicTexture#width * @type {number} * @since 3.60.0 */ this.width = -1; /** * The height of this Dynamic Texture. * * Treat this property as read-only. Use the `setSize` method to change the size. * * @name Phaser.Textures.DynamicTexture#height * @type {number} * @since 3.60.0 */ this.height = -1; /** * An array of commands that are used to draw to this Dynamic Texture. * This is flushed by the `render` method. * The `clear` method will also clear this array, then store itself. * * @name Phaser.Textures.DynamicTexture#commandBuffer * @type {array} * @since 4.0.0 */ this.commandBuffer = []; /** * A reference to the Rendering Context belonging to the Canvas Element this Dynamic Texture is drawing to. * * @name Phaser.Textures.DynamicTexture#canvas * @type {HTMLCanvasElement} * @since 3.2.0 */ this.canvas = (isCanvas) ? source : null; /** * The 2D Canvas Rendering Context. * * @name Phaser.Textures.DynamicTexture#context * @readonly * @type {CanvasRenderingContext2D} * @since 3.7.0 */ this.context = (isCanvas) ? source.getContext('2d', { willReadFrequently: true }) : null; /** * An internal Camera that can be used to move around this Dynamic Texture. * * Control it just like you would any Scene Camera. The difference is that it only impacts * the placement of **Game Objects** (not textures) that you then draw to this texture. * * You can scroll, zoom and rotate this Camera. * * @name Phaser.Textures.DynamicTexture#camera * @type {Phaser.Cameras.Scene2D.Camera} * @since 3.12.0 */ this.camera = new Camera(0, 0, width, height).setScene(manager.game.scene.systemScene, false); /** * The drawing context of this Dynamic Texture. * This contains the framebuffer that the Dynamic Texture is drawing to. * * @name Phaser.Textures.DynamicTexture#drawingContext * @type {Phaser.Renderer.WebGL.DrawingContext} * @since 4.0.0 */ this.drawingContext = isCanvas ? null : new DrawingContext(renderer, { width: width, height: height, camera: this.camera, autoClear: false, enableMipmap: true }); if (!isCanvas) { var frame = this.get(); frame.source.glTexture = this.drawingContext.texture; } this.setSize(width, height, forceEven); }, /** * Resizes this Dynamic Texture to the new dimensions given. * * In WebGL it will destroy and then re-create the frame buffer being used by this Dynamic Texture. * In Canvas it will resize the underlying canvas DOM element. * * Both approaches will erase everything currently drawn to this texture. * * If the dimensions given are the same as those already being used, calling this method will do nothing. * * @method Phaser.Textures.DynamicTexture#setSize * @since 3.10.0 * * @param {number} width - The new width of this Dynamic Texture. * @param {number} [height=width] - The new height of this Dynamic Texture. If not specified, will be set the same as the `width`. * @param {boolean} [forceEven=true] - Force the given width and height to be rounded to even values. This significantly improves the rendering quality. Set to false if you know you need an odd sized texture. * * @return {this} This Dynamic Texture. */ setSize: function (width, height, forceEven) { if (height === undefined) { height = width; } if (forceEven === undefined) { forceEven = true; } if (forceEven) { width = Math.floor(width); height = Math.floor(height); if (width % 2 !== 0) { width++; } if (height % 2 !== 0) { height++; } } var frame = this.get(); var source = frame.source; if (width !== this.width || height !== this.height) { if (this.canvas) { this.canvas.width = width; this.canvas.height = height; } var drawingContext = this.drawingContext; if (drawingContext && (drawingContext.width !== width || drawingContext.height !== height)) { drawingContext.resize(width, height); } this.camera.setSize(width, height); source.updateSize(width, height); frame.setSize(width, height); this.width = width; this.height = height; } else { // Resize the frame var baseFrame = this.getSourceImage(); if (frame.cutX + width > baseFrame.width) { width = baseFrame.width - frame.cutX; } if (frame.cutY + height > baseFrame.height) { height = baseFrame.height - frame.cutY; } frame.setSize(width, height, frame.cutX, frame.cutY); } return this; }, /** * Render the buffered drawing commands to this Dynamic Texture. * You must do this in order to see anything drawn to it. * * @method Phaser.Textures.DynamicTexture#render * @since 4.0.0 * @return {this} This Dynamic Texture instance. */ render: function () { if (this.commandBuffer.length === 0) { return; } this.camera.preRender(); if (this.renderer.type === CONST.WEBGL) { this._renderWebGL(); } if (this.renderer.type === CONST.CANVAS) { this._renderCanvas(); } return this; }, /** * Render the DynamicTexture using the WebGL render system. * * @method Phaser.Textures.DynamicTexture#_renderWebGL * @since 4.0.0 * @private */ _renderWebGL: function () { this.renderer.renderNodes.getNode('DynamicTextureHandler').run(this); }, /** * Render the DynamicTexture using the Canvas render system. * * @method Phaser.Textures.DynamicTexture#_renderCanvas * @since 4.0.0 * @private */ _renderCanvas: function () { var camera = this.camera; var context = this.context; var renderer = this.renderer; var textureManager = this.manager; renderer.setContext(context); // Big list of reused variables. var alpha, blendMode, blendModePrev, frame, height, key, originX, originY, rotation, scaleX, scaleY, tint, width, x, y; // Traverse commands. var commandBuffer = this.commandBuffer; var commandBufferLength = commandBuffer.length; var preserveBuffer = false; var eraseMode = false; for (var index = 0; index < commandBufferLength; index++) { var command = commandBuffer[index]; switch (command) { case DynamicTextureCommands.CLEAR: { x = commandBuffer[++index]; y = commandBuffer[++index]; width = commandBuffer[++index]; height = commandBuffer[++index]; if (x !== undefined && y !== undefined && width !== undefined && height !== undefined) { context.clearRect(x, y, width, height); } else { context.save(); context.setTransform(1, 0, 0, 1, 0, 0); context.clearRect(0, 0, this.width, this.height); context.restore(); } break; } case DynamicTextureCommands.FILL: { var color = commandBuffer[++index]; x = commandBuffer[++index]; y = commandBuffer[++index]; width = commandBuffer[++index]; height = commandBuffer[++index]; alpha = (color >> 24 & 0xFF) / 255; var r = (color >> 16 & 0xFF); var g = (color >> 8 & 0xFF); var b = (color & 0xFF); context.save(); context.globalCompositeOperation = 'source-over'; context.fillStyle = 'rgba(' + r + ',' + g + ',' + b + ',' + alpha + ')'; context.fillRect(x, y, width, height); context.restore(); break; } case DynamicTextureCommands.STAMP: { key = commandBuffer[++index]; frame = commandBuffer[++index]; x = commandBuffer[++index]; y = commandBuffer[++index]; alpha = commandBuffer[++index]; tint = commandBuffer[++index]; rotation = commandBuffer[++index]; scaleX = commandBuffer[++index]; scaleY = commandBuffer[++index]; originX = commandBuffer[++index]; originY = commandBuffer[++index]; blendMode = commandBuffer[++index]; if (eraseMode) { blendMode = BlendModes.ERASE; } var stamp = textureManager.resetStamp(alpha, tint); stamp.setPosition(x, y) .setRotation(rotation) .setScale(scaleX, scaleY) .setTexture(key, frame) .setOrigin(originX, originY) .setBlendMode(blendMode); stamp.renderCanvas(renderer, stamp, camera, null); break; } case DynamicTextureCommands.REPEAT: { key = commandBuffer[++index]; frame = commandBuffer[++index]; x = commandBuffer[++index]; y = commandBuffer[++index]; alpha = commandBuffer[++index]; tint = commandBuffer[++index]; rotation = commandBuffer[++index]; scaleX = commandBuffer[++index]; scaleY = commandBuffer[++index]; originX = commandBuffer[++index]; originY = commandBuffer[++index]; blendMode = commandBuffer[++index]; width = commandBuffer[++index]; height = commandBuffer[++index]; var tilePositionX = commandBuffer[++index]; var tilePositionY = commandBuffer[++index]; var tileRotation = commandBuffer[++index]; var tileScaleX = commandBuffer[++index]; var tileScaleY = commandBuffer[++index]; if (eraseMode) { blendMode = BlendModes.ERASE; } var repeat = textureManager.resetTileSprite(alpha, tint); repeat.setPosition(x, y) .setRotation(rotation) .setScale(scaleX, scaleY) .setTexture(key, frame) .setSize(width, height) .setOrigin(originX, originY) .setBlendMode(blendMode) .setTilePosition(tilePositionX, tilePositionY) .setTileRotation(tileRotation) .setTileScale(tileScaleX, tileScaleY); repeat.renderCanvas(renderer, repeat, camera, null); break; } case DynamicTextureCommands.DRAW: { var object = commandBuffer[++index]; x = commandBuffer[++index]; y = commandBuffer[++index]; if (x !== undefined) { var prevX = object.x; object.x = x; } if (y !== undefined) { var prevY = object.y; object.y = y; } if (eraseMode) { blendModePrev = object.blendMode; object.blendMode = BlendModes.ERASE; } object.renderCanvas(renderer, object, camera, null); if (x !== undefined) { object.x = prevX; } if (y !== undefined) { object.y = prevY; } if (eraseMode) { object.blendMode = blendModePrev; } break; } case DynamicTextureCommands.SET_ERASE: { eraseMode = !!commandBuffer[++index]; break; } case DynamicTextureCommands.PRESERVE: { preserveBuffer = commandBuffer[++index]; break; } case DynamicTextureCommands.CALLBACK: { var callback = commandBuffer[++index]; callback(); break; } case DynamicTextureCommands.CAPTURE: { object = commandBuffer[++index]; var config = commandBuffer[++index]; var cacheConfig = this.startCapture(object, config); object.renderCanvas( renderer, object, config.camera || camera, cacheConfig.transform ); this.finishCapture(object, cacheConfig); break; } } } if (!preserveBuffer) { commandBuffer.length = 0; } // Finish rendering. renderer.setContext(); }, /** * Fills this Dynamic Texture with the given color. * * By default it will fill the entire texture, however you can set it to fill a specific * rectangular area by using the x, y, width and height arguments. * * The color should be given in hex format, i.e. 0xff0000 for red, 0x00ff00 for green, etc. * * @method Phaser.Textures.DynamicTexture#fill * @since 3.2.0 * * @param {number} rgb - The color to fill this Dynamic Texture with, such as 0xff0000 for red. * @param {number} [alpha=1] - The alpha value used by the fill. * @param {number} [x=0] - The left coordinate of the fill rectangle. * @param {number} [y=0] - The top coordinate of the fill rectangle. * @param {number} [width=this.width] - The width of the fill rectangle. * @param {number} [height=this.height] - The height of the fill rectangle. * * @return {this} This Dynamic Texture instance. */ fill: function (rgb, alpha, x, y, width, height) { if (alpha === undefined) { alpha = 1; } if (x === undefined) { x = 0; } if (y === undefined) { y = 0; } if (width === undefined) { width = this.width; } if (height === undefined) { height = this.height; } var r = (rgb >> 16 & 0xFF); var g = (rgb >> 8 & 0xFF); var b = (rgb & 0xFF); var color = Utils.getTintFromFloats(r / 255, g / 255, b / 255, alpha); this.commandBuffer.push( DynamicTextureCommands.FILL, color, x, y, width, height ); return this; }, /** * Clears a portion or everything from this Dynamic Texture by erasing it and resetting it back to * a blank, transparent, texture. To clear an area, specify the `x`, `y`, `width` and `height`. * * @method Phaser.Textures.DynamicTexture#clear * @since 3.2.0 * * @param {number} [x=0] - The left coordinate of the area to clear. * @param {number} [y=0] - The top coordinate of the area to clear. * @param {number} [width=this.width] - The width of the area to clear. * @param {number} [height=this.height] - The height of the area to clear. * * @return {this} This Dynamic Texture instance. */ clear: function (x, y, width, height) { if (x === undefined) { x = 0; } if (y === undefined) { y = 0; } if (width === undefined) { width = this.width; } if (height === undefined) { height = this.height; } this.commandBuffer.push(DynamicTextureCommands.CLEAR, x, y, width, height); return this; }, /** * Takes the given texture key and frame and then stamps it at the given * x and y coordinates. You can use the optional 'config' argument to provide * lots more options about how the stamp is applied, including the alpha, * tint, angle, scale and origin. * * By default, the frame will stamp on the x/y coordinates based on its center. * * If you wish to stamp from the top-left, set the config `originX` and * `originY` properties both to zero. * * This method ignores the `camera` property of the Dynamic Texture. * * @method Phaser.Textures.DynamicTexture#stamp * @since 3.60.0 * * @param {string} key - The key of the texture to be used, as stored in the Texture Manager. * @param {(string|number)} [frame] - The name or index of the frame within the Texture. Set to `null` to skip this argument if not required. * @param {number} [x=0] - The x position to draw the frame at. * @param {number} [y=0] - The y position to draw the frame at. * @param {Phaser.Types.Textures.StampConfig} [config] - The stamp configuration object, allowing you to set the alpha, tint, angle, scale and origin of the stamp. * * @return {this} This Dynamic Texture instance. */ stamp: function (key, frame, x, y, config) { if (x === undefined) { x = 0; } if (y === undefined) { y = 0; } var alpha = GetFastValue(config, 'alpha', 1); var tint = GetFastValue(config, 'tint', 0xffffff); var angle = GetFastValue(config, 'angle', 0); var rotation = GetFastValue(config, 'rotation', 0); var scale = GetFastValue(config, 'scale', 1); var scaleX = GetFastValue(config, 'scaleX', scale); var scaleY = GetFastValue(config, 'scaleY', scale); var originX = GetFastValue(config, 'originX', 0.5); var originY = GetFastValue(config, 'originY', 0.5); var blendMode = GetFastValue(config, 'blendMode', 0); if (angle !== 0) { rotation = angle * Math.PI / 180; } this.commandBuffer.push( DynamicTextureCommands.STAMP, key, frame, x, y, alpha, tint, rotation, scaleX, scaleY, originX, originY, blendMode ); return this; }, /** * Draws the given object, or an array of objects, to this Dynamic Texture using a blend mode of ERASE. * This has the effect of erasing any filled pixels present in the objects from this texture. * * This method uses the `draw` method internally, * and the parameters behave the same way. * * @method Phaser.Textures.DynamicTexture#erase * @since 3.16.0 * * @param {any} entries - Any renderable Game Object, or Group, Container, Display List, Render Texture, Texture Frame, or an array of any of these. * @param {number} [x=0] - The x position to draw the Frame at, or the offset applied to the object. * @param {number} [y=0] - The y position to draw the Frame at, or the offset applied to the object. * @param {number} [alpha=1] - The alpha value. Only used when drawing Texture Frames to this texture. Game Objects use their own alpha. * @param {number} [tint=0xffffff] - The tint color value. Only used when drawing Texture Frames to this texture. Game Objects use their own tint. WebGL only. * * @return {this} This Dynamic Texture instance. */ erase: function (entries, x, y, alpha, tint) { var commandBuffer = this.commandBuffer; var commandBufferLength = commandBuffer.length; if ( commandBuffer[commandBufferLength - 2] === DynamicTextureCommands.SET_ERASE && !commandBuffer[commandBufferLength - 1] ) { // The last command finished an ERASE operation, // so we can just extend it. commandBuffer.length -= 2; } else { commandBuffer.push(DynamicTextureCommands.SET_ERASE, true); } this.draw(entries, x, y, alpha, tint); commandBuffer.push(DynamicTextureCommands.SET_ERASE, false); return this; }, /** * Draws the given object, or an array of objects, to this Dynamic Texture. * * It can accept any of the following: * * * Any renderable Game Object, such as a Sprite, Text, Graphics or TileSprite. * * Tilemap Layers. * * A Group. The contents of which will be iterated and drawn in turn. * * A Container. The contents of which will be iterated fully, and drawn in turn. * * A Scene Display List. Pass in `Scene.children` to draw the whole list. * * Another Dynamic Texture, or a Render Texture. * * A Texture Frame instance. * * A string. This is used to look-up the texture from the Texture Manager. * * Note 1: You cannot draw a Dynamic Texture to itself. * * Note 2: GameObjects will use the camera, while textures and frames will not. * Textures and frames are drawn using the `stamp` method. * * If passing in a Group or Container it will only draw children that return `true` * when their `willRender()` method is called. I.e. a Container with 10 children, * 5 of which have `visible=false` will only draw the 5 visible ones. * * If passing in an array of Game Objects it will draw them all, regardless if * they pass a `willRender` check or not. * * You can pass in a string in which case it will look for a texture in the Texture * Manager matching that string, and draw the base frame. If you need to specify * exactly which frame to draw then use the method `drawFrame` instead. * * You can pass in the `x` and `y` coordinates to draw the objects at. The use of * the coordinates differ based on what objects are being drawn. If the object is * a Group, Container or Display List, the coordinates are _added_ to the positions * of the children. For all other types of object, the coordinates are exact. * For textures and frames, the `x` and `y` values are the middle of the texture. * * The `alpha` and `tint` values are only used by Texture Frames. * Game Objects use their own alpha and tint values when being drawn. * * @method Phaser.Textures.DynamicTexture#draw * @since 3.2.0 * * @param {any} entries - Any renderable Game Object, or Group, Container, Display List, other Render Texture, Texture Frame or an array of any of these. * @param {number} [x=0] - The x position to draw the Frame at, or the offset applied to the object. * @param {number} [y=0] - The y position to draw the Frame at, or the offset applied to the object. * @param {number} [alpha=1] - The alpha value. Only used when drawing Texture Frames to this texture. Game Objects use their own alpha. * @param {number} [tint=0xffffff] - The tint color value. Only used when drawing Texture Frames to this texture. Game Objects use their own tint. WebGL only. * * @return {this} This Dynamic Texture instance. */ draw: function (entries, x, y, alpha, tint) { if (!Array.isArray(entries)) { entries = [ entries ]; } var len = entries.length; for (var i = 0; i < len; i++) { var entry = entries[i]; if (!entry || entry === this) { continue; } if (entry.renderWebGL || entry.renderCanvas) { // Game Objects this.commandBuffer.push(DynamicTextureCommands.DRAW, entry, x, y); } else if (entry.isParent || entry.list) { // Groups / Display Lists var children = entry.getChildren(); for (var c = 0; c < children.length; c++) { var child = children[c]; if (child.willRender(this.camera)) { this.draw(child, x, y); } } } else if (typeof entry === 'string') { // Texture key this.stamp(entry, null, x, y, { alpha: alpha, tint: tint }); } else if (entry instanceof Frame) { // Texture Frame instance this.stamp(entry.texture.key, entry, x, y, { alpha: alpha, tint: tint }); } else if (Array.isArray(entry)) { // Another Array this.draw(entry, x, y, alpha, tint); } } return this; }, /** * Draws the given object to this Dynamic Texture. * This allows you to draw the object as it appears in the game world, * or with various parameter overrides in the config. * * @method Phaser.Textures.DynamicTexture#capture * @since 4.0.0 * * @param {Phaser.GameObjects.GameObject} entry - Any renderable GameObject. * @param {Phaser.Types.Textures.CaptureConfig} config - The configuration object for the capture. * * @return {this} This Dynamic Texture instance. */ capture: function (entry, config) { if (!config) { config = {}; } this.commandBuffer.push(DynamicTextureCommands.CAPTURE, entry, config); return this; }, /** * Prepares an object to be rendered using the `capture` method. * This method is called automatically during rendering. * Do not call it directly. * * @method Phaser.Textures.DynamicTexture#startCapture * @since 4.0.0 * * @param {Phaser.GameObjects.GameObject} entry - The object to set up for capture. * @param {Phaser.Types.Textures.CaptureConfig} config - The configuration object for the capture. * * @return {Phaser.Types.Textures.CaptureConfig} A configuration object containing the appropriate parent transform in `transform`, and the cached object properties in any fields that were overridden. */ startCapture: function (entry, config) { var cacheConfig = {}; // Compute the parent transform. var parentTransform = undefined; if (config.transform instanceof TransformMatrix) { parentTransform = config.transform; } else if (config.transform === 'world' && entry.parentContainer) { parentTransform = entry.parentContainer.getWorldTransformMatrix(); } if (parentTransform) { cacheConfig.transform = parentTransform; } if (config.camera) { // Ensure that parent transform exists. if (!parentTransform) { parentTransform = new TransformMatrix(); } // Ensure that camera transforms are applied. config.camera.matrixExternal.multiply(parentTransform, parentTransform); } // Cache and override the object properties. if (config.x !== undefined) { cacheConfig.x = entry.x; entry.x = config.x; } if (config.y !== undefined) { cacheConfig.y = entry.y; entry.y = config.y; } if (config.rotation !== undefined) { cacheConfig.rotation = entry.rotation; entry.rotation = config.rotation; } else if (config.angle !== undefined) { // Only apply angle if rotation is not defined. cacheConfig.rotation = entry.rotation; entry.angle = config.angle; } if (config.scaleX !== undefined) { cacheConfig.scaleX = entry.scaleX; entry.scaleX = config.scaleX; } if (config.scaleY !== undefined) { cacheConfig.scaleY = entry.scaleY; entry.scaleY = config.scaleY; } if (config.originX !== undefined) { cacheConfig.originX = entry.originX; entry.originX = config.originX; } if (config.originY !== undefined) { cacheConfig.originY = entry.originY; entry.originY = config.originY; } if (config.alpha !== undefined) { cacheConfig.alpha = entry.alpha; entry.alpha = config.alpha; } if (config.tint !== undefined) { cacheConfig.tint = entry.tint; entry.tint = config.tint; } if (config.blendMode !== undefined) { cacheConfig.blendMode = entry.blendMode; entry.blendMode = config.blendMode; } return cacheConfig; }, /** * Restores the object properties that were overridden during the `capture` method. * This method is called automatically during rendering. * Do not call it directly. * * @method Phaser.Textures.DynamicTexture#finishCapture * @since 4.0.0 * * @param {Phaser.GameObjects.GameObject} entry - The GameObject to restore the properties on. * @param {Phaser.Types.Textures.CaptureConfig} cacheConfig - The cached properties to restore. */ finishCapture: function (entry, cacheConfig) { // Restore the object properties. if (cacheConfig.x !== undefined) { entry.x = cacheConfig.x; } if (cacheConfig.y !== undefined) { entry.y = cacheConfig.y; } if (cacheConfig.rotation !== undefined) { entry.rotation = cacheConfig.rotation; } if (cacheConfig.scaleX !== undefined) { entry.scaleX = cacheConfig.scaleX; } if (cacheConfig.scaleY !== undefined) { entry.scaleY = cacheConfig.scaleY; } if (cacheConfig.originX !== undefined) { entry.originX = cacheConfig.originX; } if (cacheConfig.originY !== undefined) { entry.originY = cacheConfig.originY; } if (cacheConfig.alpha !== undefined) { entry.alpha = cacheConfig.alpha; } if (cacheConfig.tint !== undefined) { entry.tint = cacheConfig.tint; } if (cacheConfig.blendMode !== undefined) { entry.blendMode = cacheConfig.blendMode; } }, /** * Takes the given Texture Frame and draws it to this Dynamic Texture as a fill pattern, * i.e. in a grid-layout based on the frame dimensions. * It uses a `TileSprite` internally to draw the frame repeatedly. * * Textures are referenced by their string-based keys, as stored in the Texture Manager. * * You can optionally provide a position, width, height, alpha and tint value to apply to * the frames before they are drawn. The position controls the top-left where the repeating * fill will start from. The width and height control the size of the filled area. * * The position can be negative if required, but the dimensions cannot. * * This method respects the camera settings of the Dynamic Texture. * * @method Phaser.Textures.DynamicTexture#repeat * @since 3.60.0 * * @param {string} key - The key of the texture to be used, as stored in the Texture Manager. * @param {(string|number)} [frame] - The name or index of the frame within the Texture. Set to `null` to skip this argument if not required. * @param {number} [x=0] - The x position to start drawing the frames from (can be negative to offset). * @param {number} [y=0] - The y position to start drawing the frames from (can be negative to offset). * @param {number} [width=this.width] - The width of the area to repeat the frame within. Defaults to the width of this Dynamic Texture. * @param {number} [height=this.height] - The height of the area to repeat the frame within. Defaults to the height of this Dynamic Texture. * @param {Phaser.Types.GameObjects.TileSprite.TileSpriteConfig} [config] - The configuration object for the TileSprite which repeats the texture, allowing you to set further properties on it. * * @return {this} This Dynamic Texture instance. */ repeat: function (key, frame, x, y, width, height, config) { if (x === undefined) { x = 0; } if (y === undefined) { y = 0; } if (width === undefined) { width = this.width; } if (height === undefined) { height = this.height; } var alpha = GetFastValue(config, 'alpha', 1); var tint = GetFastValue(config, 'tint', 0xffffff); var angle = GetFastValue(config, 'angle', 0); var rotation = GetFastValue(config, 'rotation', 0); var scale = GetFastValue(config, 'scale', 1); var scaleX = GetFastValue(config, 'scaleX', scale); var scaleY = GetFastValue(config, 'scaleY', scale); var originX = GetFastValue(config, 'originX', 0); var originY = GetFastValue(config, 'originY', 0); var blendMode = GetFastValue(config, 'blendMode', 0); if (angle !== 0) { rotation = angle * Math.PI / 180; } var tilePositionX = GetFastValue(config, 'tilePositionX', 0); var tilePositionY = GetFastValue(config, 'tilePositionY', 0); var tileRotation = GetFastValue(config, 'tileRotation', 0); var tileScaleX = GetFastValue(config, 'tileScaleX', 1); var tileScaleY = GetFastValue(config, 'tileScaleY', 1); this.commandBuffer.push( DynamicTextureCommands.REPEAT, key, frame, x, y, alpha, tint, rotation, scaleX, scaleY, originX, originY, blendMode, width, height, tilePositionX, tilePositionY, tileRotation, tileScaleX, tileScaleY ); return this; }, /** * Sets the preserve flag for this Dynamic Texture. * Ordinarily, after each render, the command buffer is cleared. * When this flag is set to `true`, the command buffer is preserved between renders. * This makes it possible to repeat the same drawing commands on each render. * * Make sure to call `clear()` at the start if you don't want to accumulate * drawing detail over the top of itself. * * @method Phaser.Textures.DynamicTexture#preserve * @since 4.0.0 * @param {boolean} preserve - Whether to preserve the command buffer after rendering. * @return {this} This Dynamic Texture instance. */ preserve: function (preserve) { this.commandBuffer.push(DynamicTextureCommands.PRESERVE, preserve); return this; }, /** * Adds a callback to run during the render process. * This callback runs as a step in the command buffer. * It can be used to set up conditions for the next draw step. * * Note that this will only execute after `render()` is called. * * @method Phaser.Textures.DynamicTexture#callback * @since 4.0.0 * @param {Function} callback - A callback function to run during the render process. * @return {this} This Dynamic Texture instance. */ callback: function (callback) { this.commandBuffer.push(DynamicTextureCommands.CALLBACK, callback); return this; }, /** * Takes a snapshot of the given area of this Dynamic Texture. * * The snapshot is taken immediately, but the results are returned via the given callback. * * To capture the whole Dynamic Texture see the `snapshot` method. * To capture just a specific pixel, see the `snapshotPixel` method. * * Snapshots work by using the WebGL `readPixels` feature to grab every pixel from the frame buffer * into an ArrayBufferView. It then parses this, copying the contents to a temporary Canvas and finally * creating an Image object from it, which is the image returned to the callback provided. * * All in all, this is a computationally expensive and blocking process, which gets more expensive * the larger the resolution this Dynamic Texture has, so please be careful how you employ this in your game. * * @method Phaser.Textures.DynamicTexture#snapshotArea * @since 3.19.0 * * @param {number} x - The x coordinate to grab from. * @param {number} y - The y coordinate to grab from. * @param {number} width - The width of the area to grab. * @param {number} height - The height of the area to grab. * @param {Phaser.Types.Renderer.Snapshot.SnapshotCallback} callback - The Function to invoke after the snapshot image is created. * @param {string} [type='image/png'] - The format of the image to create, usually `image/png` or `image/jpeg`. * @param {number} [encoderOptions=0.92] - The image quality, between 0 and 1. Used for image formats with lossy compression, such as `image/jpeg`. * * @return {this} This Dynamic Texture instance. */ snapshotArea: function (x, y, width, height, callback, type, encoderOptions) { if (this.drawingContext) { this.renderer.snapshotFramebuffer(this.drawingContext.framebuffer, this.width, this.height, callback, false, x, y, width, height, type, encoderOptions); } else { this.renderer.snapshotCanvas(this.canvas, callback, false, x, y, width, height, type, encoderOptions); } return this; }, /** * Takes a snapshot of the whole of this Dynamic Texture. * * The snapshot is taken immediately, but the results are returned via the given callback. * * To capture a portion of this Dynamic Texture see the `snapshotArea` method. * To capture just a specific pixel, see the `snapshotPixel` method. * * Snapshots work by using the WebGL `readPixels` feature to grab every pixel from the frame buffer * into an ArrayBufferView. It then parses this, copying the contents to a temporary Canvas and finally * creating an Image object from it, which is the image returned to the callback provided. * * All in all, this is a computationally expensive and blocking process, which gets more expensive * the larger the resolution this Dynamic Texture has, so please be careful how you employ this in your game. * * @method Phaser.Textures.DynamicTexture#snapshot * @since 3.19.0 * * @param {Phaser.Types.Renderer.Snapshot.SnapshotCallback} callback - The Function to invoke after the snapshot image is created. * @param {string} [type='image/png'] - The format of the image to create, usually `image/png` or `image/jpeg`. * @param {number} [encoderOptions=0.92] - The image quality, between 0 and 1. Used for image formats with lossy compression, such as `image/jpeg`. * * @return {this} This Dynamic Texture instance. */ snapshot: function (callback, type, encoderOptions) { return this.snapshotArea(0, 0, this.width, this.height, callback, type, encoderOptions); }, /** * Takes a snapshot of the given pixel from this Dynamic Texture. * * The snapshot is taken immediately, but the results are returned via the given callback. * * To capture the whole Dynamic Texture see the `snapshot` method. * To capture a portion of this Dynamic Texture see the `snapshotArea` method. * * Unlike the two other snapshot methods, this one will send your callback a `Color` object * containing the color data for the requested pixel. It doesn't need to create an internal * Canvas or Image object, so is a lot faster to execute, using less memory than the other snapshot methods. * * @method Phaser.Textures.DynamicTexture#snapshotPixel * @since 3.19.0 * * @param {number} x - The x coordinate of the pixel to get. * @param {number} y - The y coordinate of the pixel to get. * @param {Phaser.Types.Renderer.Snapshot.SnapshotCallback} callback - The Function to invoke after the snapshot pixel data is extracted. * * @return {this} This Dynamic Texture instance. */ snapshotPixel: function (x, y, callback) { return this.snapshotArea(x, y, 1, 1, callback, 'pixel'); }, /** * Returns the underlying WebGLTextureWrapper, if not running in Canvas mode. * * @method Phaser.Textures.DynamicTexture#getWebGLTexture * @since 3.60.0 * * @return {?Phaser.Renderer.WebGL.Wrappers.WebGLTextureWrapper} The underlying WebGLTextureWrapper, if not running in Canvas mode. */ getWebGLTexture: function () { if (this.drawingContext) { return this.drawingContext.texture; } }, /** * Sets this Dynamic Texture onto the TextureManager.Stamp * and then calls its render method. * * @method Phaser.Textures.DynamicTexture#renderWebGL * @since 3.60.0 * * @param {Phaser.Renderer.WebGL.WebGLRenderer} renderer - A reference to the current active WebGL renderer. * @param {Phaser.GameObjects.Image} src - The Game Object being rendered in this call. * @param {Phaser.Cameras.Scene2D.Camera} camera - The Camera that is rendering the Game Object. * @param {Phaser.GameObjects.Components.TransformMatrix} parentMatrix - This transform matrix is defined if the game object is nested */ renderWebGL: function (renderer, src, camera, parentMatrix) { var stamp = this.manager.resetStamp(); stamp.setTexture(this); stamp.setOrigin(0); stamp.renderWebGLStep(renderer, stamp, camera, parentMatrix); }, /** * This is a NOOP method. Dynamic Textures cannot render themselves to the Canvas Renderer directly. * * @method Phaser.Textures.DynamicTexture#renderCanvas * @since 3.60.0 * * @param {(Phaser.Renderer.Canvas.CanvasRenderer|Phaser.Renderer.WebGL.WebGLRenderer)} renderer - The Canvas Renderer which would be rendered to. * @param {Phaser.GameObjects.GameObject} mask - The masked Game Object which would be rendered. * @param {Phaser.Cameras.Scene2D.Camera} camera - The Camera to render to. */ renderCanvas: function () { // NOOP }, /** * Destroys this Texture and releases references to its sources and frames. * * @method Phaser.Textures.DynamicTexture#destroy * @since 3.60.0 */ destroy: function () { var stamp = this.manager.stamp; if (stamp && stamp.texture === this) { this.manager.resetStamp(); } Texture.prototype.destroy.call(this); CanvasPool.remove(this.canvas); if (this.drawingContext) { this.drawingContext.destroy(); } this.camera.destroy(); this.canvas = null; this.context = null; this.renderer = null; } }); module.exports = DynamicTexture;