/**
 * @author       Richard Davey <rich@photonstorm.com>
 * @copyright    2013-2023 Photon Storm Ltd.
 * @license      {@link https://opensource.org/licenses/MIT|MIT License}
 */

var AnimationState = require('../../animations/AnimationState');
var Class = require('../../utils/Class');
var GenerateGridVerts = require('../../geom/mesh/GenerateGridVerts');
var IntegerToRGB = require('../../display/color/IntegerToRGB');
var Mesh = require('../mesh/Mesh');
var UUID = require('../../utils/string/UUID');

/**
 * @classdesc
 * A Plane Game Object.
 *
 * The Plane Game Object is a helper class that takes the Mesh Game Object and extends it,
 * allowing for fast and easy creation of Planes. A Plane is a one-sided grid of cells,
 * where you specify the number of cells in each dimension. The Plane can have a texture
 * that is either repeated (tiled) across each cell, or applied to the full Plane.
 *
 * The Plane can then be manipulated in 3D space, with rotation across all 3 axis.
 *
 * This allows you to create effects not possible with regular Sprites, such as perspective
 * distortion. You can also adjust the vertices on a per-vertex basis. Plane data becomes
 * part of the WebGL batch, just like standard Sprites, so doesn't introduce any additional
 * shader overhead. Because the Plane just generates vertices into the WebGL batch, like any
 * other Sprite, you can use all of the common Game Object components on a Plane too,
 * such as a custom pipeline, mask, blend mode or texture.
 *
 * You can use the `uvScroll` and `uvScale` methods to adjust the placement and scaling
 * of the texture if this Plane is using a single texture, and not a frame from a texture
 * atlas or sprite sheet.
 *
 * The Plane Game Object also has the Animation component, allowing you to play animations
 * across the Plane just as you would with a Sprite.
 *
 * Note that the Plane object is WebGL only and does not have a Canvas counterpart.
 *
 * The Plane origin is always 0.5 x 0.5 and cannot be changed.
 *
 * @class Plane
 * @extends Phaser.GameObjects.Mesh
 * @memberof Phaser.GameObjects
 * @constructor
 * @since 3.60.0
 *
 * @param {Phaser.Scene} scene - The Scene to which this Plane belongs. A Plane can only belong to one Scene at a time.
 * @param {number} [x] - The horizontal position of this Plane in the world.
 * @param {number} [y] - The vertical position of this Plane in the world.
 * @param {string|Phaser.Textures.Texture} [texture] - The key, or instance of the Texture this Plane will use to render with, as stored in the Texture Manager.
 * @param {string|number} [frame] - An optional frame from the Texture this Plane is rendering with.
 * @param {number} [width=8] - The width of this Plane, in cells, not pixels.
 * @param {number} [height=8] - The height of this Plane, in cells, not pixels.
 * @param {boolean} [tile=false] - Is the texture tiled? I.e. repeated across each cell.
 */
var Plane = new Class({

    Extends: Mesh,

    initialize:

    function Plane (scene, x, y, texture, frame, width, height, tile)
    {
        if (!texture) { texture = '__DEFAULT'; }

        Mesh.call(this, scene, x, y, texture, frame);

        this.type = 'Plane';

        /**
         * The Animation State component of this Sprite.
         *
         * This component provides features to apply animations to this Sprite.
         * It is responsible for playing, loading, queuing animations for later playback,
         * mixing between animations and setting the current animation frame to this Sprite.
         *
         * @name Phaser.GameObjects.Plane#anims
         * @type {Phaser.Animations.AnimationState}
         * @since 3.60.0
         */
        this.anims = new AnimationState(this);

        /**
         * The width of this Plane in cells, not pixels.
         *
         * This value is read-only. To adjust it, see the `setGridSize` method.
         *
         * @name Phaser.GameObjects.Plane#gridWidth
         * @type {number}
         * @readonly
         * @since 3.60.0
         */
        this.gridWidth;

        /**
         * The height of this Plane in cells, not pixels.
         *
         * This value is read-only. To adjust it, see the `setGridSize` method.
         *
         * @name Phaser.GameObjects.Plane#gridHeight
         * @type {number}
         * @readonly
         * @since 3.60.0
         */
        this.gridHeight;

        /**
         * Is the texture of this Plane tiled across all cells, or not?
         *
         * This value is read-only. To adjust it, see the `setGridSize` method.
         *
         * @name Phaser.GameObjects.Plane#isTiled
         * @type {boolean}
         * @readonly
         * @since 3.60.0
         */
        this.isTiled;

        /**
         * If this Plane has a checkboard texture, this is a reference to
         * the WebGLTexture being used. Otherwise, it's null.
         *
         * @name Phaser.GameObjects.Plane#_checkerboard
         * @type {?WebGLTexture}
         * @private
         * @since 3.60.0
         */
        this._checkerboard = null;

        this.hideCCW = false;

        this.setGridSize(width, height, tile);
        this.setSizeToFrame(false);
        this.setViewHeight();
    },

    /**
     * Modifies the layout of this Plane by adjusting the grid dimensions to the
     * given width and height. The values are given in cells, not pixels.
     *
     * The `tile` parameter allows you to control if the texture is tiled, or
     * applied across the entire Plane? A tiled texture will repeat with one
     * iteration per cell. A non-tiled texture will be applied across the whole
     * Plane.
     *
     * Note that if this Plane is using a single texture, not from a texture atlas
     * or sprite sheet, then you can use the `Plane.uvScale` method to have much
     * more fine-grained control over the texture tiling.
     *
     * @method Phaser.GameObjects.Plane#preDestroy
     * @since 3.60.0
     *
     * @param {number} [width=8] - The width of this Plane, in cells, not pixels.
     * @param {number} [height=8] - The height of this Plane, in cells, not pixels.
     * @param {boolean} [tile=false] - Is the texture tiled? I.e. repeated across each cell.
     */
    setGridSize: function (width, height, tile)
    {
        if (width === undefined) { width = 8; }
        if (height === undefined) { height = 8; }
        if (tile === undefined) { tile = false; }

        var flipY = false;

        if (tile)
        {
            flipY = true;
        }

        this.gridWidth = width;
        this.gridHeight = height;
        this.isTiled = tile;

        this.clear();

        GenerateGridVerts({
            mesh: this,
            widthSegments: width,
            heightSegments: height,
            isOrtho: false,
            tile: tile,
            flipY: flipY
        });

        return this;
    },

    /**
     * An internal method that resets the perspective projection for this Plane
     * when it changes texture or frame, and also resets the cell UV coordinates,
     * if required.
     *
     * @method Phaser.GameObjects.Plane#setSizeToFrame
     * @since 3.60.0
     * @override
     *
     * @param {boolean} [resetUV=true] - Reset all of the cell UV coordinates?
     *
     * @return {this} This Game Object instance.
     */
    setSizeToFrame: function (resetUV)
    {
        if (resetUV === undefined) { resetUV = true; }

        var frame = this.frame;

        this.setPerspective(this.width / frame.width, this.height / frame.height);

        if (this._checkerboard && this._checkerboard !== this.texture)
        {
            this.removeCheckerboard();
        }

        if (!resetUV)
        {
            return this;
        }

        //  Reset UV coordinates if frame has changed

        var gridX = this.gridWidth;
        var gridY = this.gridHeight;

        var verts = this.vertices;

        var frameU0 = frame.u0;
        var frameU1 = frame.u1;
        var frameV0 = frame.v0;
        var frameV1 = frame.v1;

        var x;
        var y;
        var i = 0;

        if (this.isTiled)
        {
            //  flipY
            frameV0 = frame.v1;
            frameV1 = frame.v0;

            for (y = 0; y < gridY; y++)
            {
                for (x = 0; x < gridX; x++)
                {
                    verts[i++].setUVs(frameU0, frameV1);
                    verts[i++].setUVs(frameU0, frameV0);
                    verts[i++].setUVs(frameU1, frameV1);
                    verts[i++].setUVs(frameU0, frameV0);
                    verts[i++].setUVs(frameU1, frameV0);
                    verts[i++].setUVs(frameU1, frameV1);
                }
            }
        }
        else
        {
            var gridX1 = gridX + 1;
            var gridY1 = gridY + 1;

            var frameU = frameU1 - frameU0;
            var frameV = frameV1 - frameV0;

            var uvs = [];

            for (y = 0; y < gridY1; y++)
            {
                for (x = 0; x < gridX1; x++)
                {
                    var tu = frameU0 + frameU * (x / gridX);
                    var tv = frameV0 + frameV * (y / gridY);

                    uvs.push(tu, tv);
                }
            }

            for (y = 0; y < gridY; y++)
            {
                for (x = 0; x < gridX; x++)
                {
                    var a = (x + gridX1 * y) * 2;
                    var b = (x + gridX1 * (y + 1)) * 2;
                    var c = ((x + 1) + gridX1 * (y + 1)) * 2;
                    var d = ((x + 1) + gridX1 * y) * 2;

                    verts[i++].setUVs(uvs[a], uvs[a + 1]);
                    verts[i++].setUVs(uvs[b], uvs[b + 1]);
                    verts[i++].setUVs(uvs[d], uvs[d + 1]);
                    verts[i++].setUVs(uvs[b], uvs[b + 1]);
                    verts[i++].setUVs(uvs[c], uvs[c + 1]);
                    verts[i++].setUVs(uvs[d], uvs[d + 1]);
                }
            }
        }

        return this;
    },

    /**
     * Sets the height of this Plane to match the given value, in pixels.
     *
     * This adjusts the `Plane.viewPosition.z` value to achieve this.
     *
     * If no `value` parameter is given, it will set the view height to match
     * that of the current texture frame the Plane is using.
     *
     * @method Phaser.GameObjects.Plane#setViewHeight
     * @since 3.60.0
     *
     * @param {number} [value] - The height, in pixels, to set this Plane view height to.
     */
    setViewHeight: function (value)
    {
        if (value === undefined) { value = this.frame.height; }

        var vFOV = this.fov * (Math.PI / 180);

        this.viewPosition.z = (this.height / value) / (Math.tan(vFOV / 2));

        this.dirtyCache[10] = 1;
    },

    /**
     * Creates a checkerboard style texture, based on the given colors and alpha
     * values and applies it to this Plane, replacing any current texture it may
     * have.
     *
     * The colors are used in an alternating pattern, like a chess board.
     *
     * Calling this method generates a brand new 16x16 pixel WebGLTexture internally
     * and applies it to this Plane. While quite fast to do, you should still be
     * mindful of calling this method either extensively, or in tight parts of
     * your game.
     *
     * @method Phaser.GameObjects.Plane#createCheckerboard
     * @since 3.60.0
     *
     * @param {number} [color1=0xffffff] - The odd cell color, specified as a hex value.
     * @param {number} [color2=0x0000ff] - The even cell color, specified as a hex value.
     * @param {number} [alpha1=255] - The odd cell alpha value, specified as a number between 0 and 255.
     * @param {number} [alpha2=255] - The even cell alpha value, specified as a number between 0 and 255.
     * @param {number} [height=128] - The view height of the Plane after creation, in pixels.
     */
    createCheckerboard: function (color1, color2, alpha1, alpha2, height)
    {
        if (color1 === undefined) { color1 = 0xffffff; }
        if (color2 === undefined) { color2 = 0x0000ff; }
        if (alpha1 === undefined) { alpha1 = 255; }
        if (alpha2 === undefined) { alpha2 = 255; }
        if (height === undefined) { height = 128; }

        var gl = this.scene.sys.renderer.gl;

        var glTexture = gl.createTexture();

        gl.activeTexture(gl.TEXTURE0);

        gl.bindTexture(gl.TEXTURE_2D, glTexture);

        gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
        gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);

        //  Let's assume 16x16 for our texture size and 8x8 cell size

        var c1 = IntegerToRGB(color1);
        var c2 = IntegerToRGB(color2);

        var colors = [];

        for (var h = 0; h < 16; h++)
        {
            for (var w = 0; w < 16; w++)
            {
                if ((h < 8 && w < 8) || (h > 7 && w > 7))
                {
                    colors.push(c1.r, c1.g, c1.b, alpha1);
                }
                else
                {
                    colors.push(c2.r, c2.g, c2.b, alpha2);
                }
            }
        }

        gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, 16, 16, 0, gl.RGBA, gl.UNSIGNED_BYTE, new Uint8Array(colors));

        glTexture.isAlphaPremultiplied = true;
        glTexture.isRenderTexture = false;
        glTexture.width = 16;
        glTexture.height = 16;

        var texture = this.scene.sys.textures.addGLTexture(UUID(), glTexture, 16, 16);

        this.removeCheckerboard();

        this._checkerboard = texture;

        gl.bindTexture(gl.TEXTURE_2D, null);

        this.setTexture(texture);

        this.setSizeToFrame();

        this.setViewHeight(height);

        return this;
    },

    /**
     * If this Plane has a Checkerboard Texture, this method will destroy it
     * and reset the internal flag for it.
     *
     * @method Phaser.GameObjects.Plane#removeCheckerboard
     * @since 3.60.0
     */
    removeCheckerboard: function ()
    {
        if (this._checkerboard)
        {
            this._checkerboard.destroy();

            this._checkerboard = null;
        }
    },

    /**
     * Start playing the given animation on this Plane.
     *
     * Animations in Phaser can either belong to the global Animation Manager, or specifically to this Plane.
     *
     * The benefit of a global animation is that multiple Game Objects can all play the same animation, without
     * having to duplicate the data. You can just create it once and then play it on any animating Game Object.
     *
     * The following code shows how to create a global repeating animation. The animation will be created
     * from all of the frames within the sprite sheet that was loaded with the key 'muybridge':
     *
     * ```javascript
     * var config = {
     *     key: 'run',
     *     frames: 'muybridge',
     *     frameRate: 15,
     *     repeat: -1
     * };
     *
     * //  This code should be run from within a Scene:
     * this.anims.create(config);
     * ```
     *
     * However, if you wish to create an animation that is unique to this Plane, and this Plane alone,
     * you can call the `Animation.create` method instead. It accepts the exact same parameters as when
     * creating a global animation, however the resulting data is kept locally in this Plane.
     *
     * With the animation created, either globally or locally, you can now play it on this Plane:
     *
     * ```javascript
     * const plane = this.add.plane(...);
     * plane.play('run');
     * ```
     *
     * Alternatively, if you wish to run it at a different frame rate for example, you can pass a config
     * object instead:
     *
     * ```javascript
     * const plane = this.add.plane(...);
     * plane.play({ key: 'run', frameRate: 24 });
     * ```
     *
     * When playing an animation on a Plane it will first check to see if it can find a matching key
     * locally within the Plane. If it can, it will play the local animation. If not, it will then
     * search the global Animation Manager and look for it there.
     *
     * If you need a Plane to be able to play both local and global animations, make sure they don't
     * have conflicting keys.
     *
     * See the documentation for the `PlayAnimationConfig` config object for more details about this.
     *
     * Also, see the documentation in the Animation Manager for further details on creating animations.
     *
     * @method Phaser.GameObjects.Plane#play
     * @fires Phaser.Animations.Events#ANIMATION_START
     * @since 3.60.0
     *
     * @param {(string|Phaser.Animations.Animation|Phaser.Types.Animations.PlayAnimationConfig)} key - The string-based key of the animation to play, or an Animation instance, or a `PlayAnimationConfig` object.
     * @param {boolean} [ignoreIfPlaying=false] - If an animation is already playing then ignore this call.
     *
     * @return {this} This Game Object.
     */
    play: function (key, ignoreIfPlaying)
    {
        return this.anims.play(key, ignoreIfPlaying);
    },

    /**
     * Start playing the given animation on this Plane, in reverse.
     *
     * Animations in Phaser can either belong to the global Animation Manager, or specifically to a Game Object.
     *
     * The benefit of a global animation is that multiple Game Objects can all play the same animation, without
     * having to duplicate the data. You can just create it once and then play it on any animating Game Object.
     *
     * The following code shows how to create a global repeating animation. The animation will be created
     * from all of the frames within the sprite sheet that was loaded with the key 'muybridge':
     *
     * ```javascript
     * var config = {
     *     key: 'run',
     *     frames: 'muybridge',
     *     frameRate: 15,
     *     repeat: -1
     * };
     *
     * //  This code should be run from within a Scene:
     * this.anims.create(config);
     * ```
     *
     * However, if you wish to create an animation that is unique to this Game Object, and this Game Object alone,
     * you can call the `Animation.create` method instead. It accepts the exact same parameters as when
     * creating a global animation, however the resulting data is kept locally in this Game Object.
     *
     * With the animation created, either globally or locally, you can now play it on this Game Object:
     *
     * ```javascript
     * const plane = this.add.plane(...);
     * plane.playReverse('run');
     * ```
     *
     * Alternatively, if you wish to run it at a different frame rate, for example, you can pass a config
     * object instead:
     *
     * ```javascript
     * const plane = this.add.plane(...);
     * plane.playReverse({ key: 'run', frameRate: 24 });
     * ```
     *
     * When playing an animation on a Game Object it will first check to see if it can find a matching key
     * locally within the Game Object. If it can, it will play the local animation. If not, it will then
     * search the global Animation Manager and look for it there.
     *
     * If you need a Game Object to be able to play both local and global animations, make sure they don't
     * have conflicting keys.
     *
     * See the documentation for the `PlayAnimationConfig` config object for more details about this.
     *
     * Also, see the documentation in the Animation Manager for further details on creating animations.
     *
     * @method Phaser.GameObjects.Plane#playReverse
     * @fires Phaser.Animations.Events#ANIMATION_START
     * @since 3.60.0
     *
     * @param {(string|Phaser.Animations.Animation|Phaser.Types.Animations.PlayAnimationConfig)} key - The string-based key of the animation to play, or an Animation instance, or a `PlayAnimationConfig` object.
     * @param {boolean} [ignoreIfPlaying=false] - If an animation is already playing then ignore this call.
     *
     * @return {this} This Game Object.
     */
    playReverse: function (key, ignoreIfPlaying)
    {
        return this.anims.playReverse(key, ignoreIfPlaying);
    },

    /**
     * Waits for the specified delay, in milliseconds, then starts playback of the given animation.
     *
     * If the animation _also_ has a delay value set in its config, it will be **added** to the delay given here.
     *
     * If an animation is already running and a new animation is given to this method, it will wait for
     * the given delay before starting the new animation.
     *
     * If no animation is currently running, the given one begins after the delay.
     *
     * When playing an animation on a Game Object it will first check to see if it can find a matching key
     * locally within the Game Object. If it can, it will play the local animation. If not, it will then
     * search the global Animation Manager and look for it there.
     *
     * @method Phaser.GameObjects.Plane#playAfterDelay
     * @fires Phaser.Animations.Events#ANIMATION_START
     * @since 3.60.0
     *
     * @param {(string|Phaser.Animations.Animation|Phaser.Types.Animations.PlayAnimationConfig)} key - The string-based key of the animation to play, or an Animation instance, or a `PlayAnimationConfig` object.
     * @param {number} delay - The delay, in milliseconds, to wait before starting the animation playing.
     *
     * @return {this} This Game Object.
     */
    playAfterDelay: function (key, delay)
    {
        return this.anims.playAfterDelay(key, delay);
    },

    /**
     * Waits for the current animation to complete the `repeatCount` number of repeat cycles, then starts playback
     * of the given animation.
     *
     * You can use this to ensure there are no harsh jumps between two sets of animations, i.e. going from an
     * idle animation to a walking animation, by making them blend smoothly into each other.
     *
     * If no animation is currently running, the given one will start immediately.
     *
     * When playing an animation on a Game Object it will first check to see if it can find a matching key
     * locally within the Game Object. If it can, it will play the local animation. If not, it will then
     * search the global Animation Manager and look for it there.
     *
     * @method Phaser.GameObjects.Plane#playAfterRepeat
     * @fires Phaser.Animations.Events#ANIMATION_START
     * @since 3.60.0
     *
     * @param {(string|Phaser.Animations.Animation|Phaser.Types.Animations.PlayAnimationConfig)} key - The string-based key of the animation to play, or an Animation instance, or a `PlayAnimationConfig` object.
     * @param {number} [repeatCount=1] - How many times should the animation repeat before the next one starts?
     *
     * @return {this} This Game Object.
     */
    playAfterRepeat: function (key, repeatCount)
    {
        return this.anims.playAfterRepeat(key, repeatCount);
    },

    /**
     * Immediately stops the current animation from playing and dispatches the `ANIMATION_STOP` events.
     *
     * If no animation is playing, no event will be dispatched.
     *
     * If there is another animation queued (via the `chain` method) then it will start playing immediately.
     *
     * @method Phaser.GameObjects.Plane#stop
     * @fires Phaser.Animations.Events#ANIMATION_STOP
     * @since 3.60.0
     *
     * @return {this} This Game Object.
     */
    stop: function ()
    {
        return this.anims.stop();
    },

    /**
     * Stops the current animation from playing after the specified time delay, given in milliseconds.
     *
     * It then dispatches the `ANIMATION_STOP` event.
     *
     * If no animation is running, no events will be dispatched.
     *
     * If there is another animation in the queue (set via the `chain` method) then it will start playing,
     * when the current one stops.
     *
     * @method Phaser.GameObjects.Plane#stopAfterDelay
     * @fires Phaser.Animations.Events#ANIMATION_STOP
     * @since 3.60.0
     *
     * @param {number} delay - The number of milliseconds to wait before stopping this animation.
     *
     * @return {this} This Game Object.
     */
    stopAfterDelay: function (delay)
    {
        return this.anims.stopAfterDelay(delay);
    },

    /**
     * Stops the current animation from playing after the given number of repeats.
     *
     * It then dispatches the `ANIMATION_STOP` event.
     *
     * If no animation is running, no events will be dispatched.
     *
     * If there is another animation in the queue (set via the `chain` method) then it will start playing,
     * when the current one stops.
     *
     * @method Phaser.GameObjects.Plane#stopAfterRepeat
     * @fires Phaser.Animations.Events#ANIMATION_STOP
     * @since 3.60.0
     *
     * @param {number} [repeatCount=1] - How many times should the animation repeat before stopping?
     *
     * @return {this} This Game Object.
     */
    stopAfterRepeat: function (repeatCount)
    {
        return this.anims.stopAfterRepeat(repeatCount);
    },

    /**
     * Stops the current animation from playing when it next sets the given frame.
     * If this frame doesn't exist within the animation it will not stop it from playing.
     *
     * It then dispatches the `ANIMATION_STOP` event.
     *
     * If no animation is running, no events will be dispatched.
     *
     * If there is another animation in the queue (set via the `chain` method) then it will start playing,
     * when the current one stops.
     *
     * @method Phaser.GameObjects.Plane#stopOnFrame
     * @fires Phaser.Animations.Events#ANIMATION_STOP
     * @since 3.60.0
     *
     * @param {Phaser.Animations.AnimationFrame} frame - The frame to check before stopping this animation.
     *
     * @return {this} This Game Object.
     */
    stopOnFrame: function (frame)
    {
        return this.anims.stopOnFrame(frame);
    },

    /**
     * Runs the preUpdate for this Plane, which will check its Animation State,
     * if one is playing, and refresh view / model matrices, if updated.
     *
     * @method Phaser.GameObjects.Plane#preUpdate
     * @protected
     * @since 3.60.0
     *
     * @param {number} time - The current timestamp.
     * @param {number} delta - The delta time, in ms, elapsed since the last frame.
     */
    preUpdate: function (time, delta)
    {
        Mesh.prototype.preUpdate.call(this, time, delta);

        this.anims.update(time, delta);
    },

    /**
     * Handles the pre-destroy step for the Plane, which removes the vertices and debug callbacks.
     *
     * @method Phaser.GameObjects.Plane#preDestroy
     * @private
     * @since 3.60.0
     */
    preDestroy: function ()
    {
        this.clear();
        this.removeCheckerboard();

        this.anims.destroy();

        this.anims = undefined;

        this.debugCallback = null;
        this.debugGraphic = null;
    }

});

module.exports = Plane;


if (erase)
{
this._eraseMode = true;
}
this.draw(stamp, x, y);
if (erase)
{
this._eraseMode = false;

keep keep keep keep keep replace keep keep keep keep

/**
 * Options for the Graphics Game Object.
 *
 * @typedef {object} Phaser.Types.GameObjects.Graphics.Options
 * @extends Phaser.Types.GameObjects.Graphics.Styles
 * @since 3.0.0
 *
 * @property {number} [x] - The x coordinate of the Graphics.
 * @property {number} [y] - The y coordinate of the Graphics.
 */


* @property {number} [scaleY=1] - Set the vertical scale of the stamp. Overrides the scale property, if provided.

* @property {number} [originX=0.5] - The horizontal origin of the stamp. 0 is the left, 0.5 is the center and 1 is the right.

* @property {number} [originY=0.5] - The vertical origin of the stamp. 0 is the top, 0.5 is the center and 1 is the bottom.

* @property {(string|Phaser.BlendModes|number)} [blendMode=0] - The blend mode used when drawing the stamp. Defaults to 0 (normal).

* @property {boolean} [erase=false] - Erase this stamp from the texture?

* @property {boolean} [skipBatch=false] - Skip beginning and ending a batch with this call. Use if this is part of a bigger batched draw.

/**
 * @author       Richard Davey <rich@photonstorm.com>
 * @copyright    2013-2023 Photon Storm Ltd.
 * @license      {@link https://opensource.org/licenses/MIT|MIT License}
 */

var BlendModes = require('../renderer/BlendModes');
var Camera = require('../cameras/2d/BaseCamera');
var CanvasPool = require('../display/canvas/CanvasPool');
var Class = require('../utils/Class');
var CONST = require('../const');
var Frame = require('./Frame');
var GetFastValue = require('../utils/object/GetFastValue');
var PIPELINES = require('../renderer/webgl/pipelines/const');
var RenderTarget = require('../renderer/webgl/RenderTarget');
var Texture = require('./Texture');
var Utils = require('../renderer/webgl/Utils');

/**
 * @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');
 * ```
 *
 * 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.
 *
 * Based on the assumption that you will be using this Dynamic Texture as a source for Sprites, it will
 * automatically invert any drawing done to it on the y axis. If you do not require this, please call the
 * `setIsSpriteTexture()` method and pass it `false` as its parameter. Do this before you start drawing
 * to this texture, otherwise you will get vertically inverted frames under WebGL. This isn't required
 * for Canvas.
 *
 * @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 Dymamic Texture in pixels. Defaults to 256 x 256.
 * @param {number} [height=256] - The height of this Dymamic Texture in pixels. Defaults to 256 x 256.
 */
var DynamicTexture = new Class({

    Extends: Texture,

    initialize:

    function DynamicTexture (manager, key, width, height)
    {
        if (width === undefined) { width = 256; }
        if (height === undefined) { height = 256; }

        /**
         * 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;

        /**
         * This flag is set to 'true' during `beginDraw` and reset to 'false` in `endDraw`,
         * allowing you to determine if this Dynamic Texture is batch drawing, or not.
         *
         * @name Phaser.Textures.DynamicTexture#isDrawing
         * @type {boolean}
         * @readonly
         * @since 3.60.0
         */
        this.isDrawing = false;

        /**
         * 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;

        /**
         * Is this Dynamic Texture dirty or not? If not it won't spend time clearing or filling itself.
         *
         * @name Phaser.Textures.DynamicTexture#dirty
         * @type {boolean}
         * @since 3.12.0
         */
        this.dirty = false;

        /**
         * Is this Dynamic Texture being used as the base texture for a Sprite Game Object?
         *
         * This is enabled by default, as we expect that will be the core use for Dynamic Textures.
         *
         * However, to disable it, call `RenderTexture.setIsSpriteTexture(false)`.
         *
         * You should do this _before_ drawing to this texture, so that it correctly
         * inverses the frames for WebGL rendering. Not doing so will result in vertically flipped frames.
         *
         * This property is used in the `endDraw` method.
         *
         * @name Phaser.Textures.DynamicTexture#isSpriteTexture
         * @type {boolean}
         * @since 3.60.0
         */
        this.isSpriteTexture = true;

        /**
         * Internal erase mode flag.
         *
         * @name Phaser.Textures.DynamicTexture#_eraseMode
         * @type {boolean}
         * @private
         * @since 3.16.0
         */
        this._eraseMode = false;

        /**
         * 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.BaseCamera}
         * @since 3.12.0
         */
        this.camera = new Camera(0, 0, width, height).setScene(manager.game.scene.systemScene, false);

        /**
         * The Render Target that belongs to this Dynamic Texture.
         *
         * A Render Target encapsulates a framebuffer and texture for the WebGL Renderer.
         *
         * This property remains `null` under Canvas.
         *
         * @name Phaser.Textures.DynamicTexture#renderTarget
         * @type {Phaser.Renderer.WebGL.RenderTarget}
         * @since 3.60.0
         */
        this.renderTarget = (!isCanvas) ? new RenderTarget(renderer, width, height, 1, 0, false, true, true, false) : null;

        /**
         * A reference to the WebGL Single Pipeline.
         *
         * This property remains `null` under Canvas.
         *
         * @name Phaser.Textures.DynamicTexture#pipeline
         * @type {Phaser.Renderer.WebGL.Pipelines.SinglePipeline}
         * @since 3.60.0
         */
        this.pipeline = (!isCanvas) ? renderer.pipelines.get(PIPELINES.SINGLE_PIPELINE) : null;

        this.setSize(width, height);
    },

    /**
     * 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`.
     *
     * @return {this} This Dynamic Texture.
     */
    setSize: function (width, height)
    {
        if (height === undefined) { height = width; }

        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 renderTarget = this.renderTarget;

            if (renderTarget)
            {
                renderTarget.resize(width, height);

                frame.glTexture = renderTarget.texture;

                source.isRenderTexture = true;
                source.isGLTexture = true;
                source.glTexture = renderTarget.texture;
                source.glTexture.flipY = true;
            }

            this.camera.setSize(width, height);

            source.width = width;
            source.height = 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;
    },

    /**
     * If you are planning on using this Render Texture as a base texture for Sprite
     * Game Objects, then you should call this method with a value of `true` before
     * drawing anything to it, otherwise you will get inverted frames in WebGL.
     *
     * @method Phaser.Textures.DynamicTexture#setIsSpriteTexture
     * @since 3.60.0
     *
     * @param {boolean} value - Is this Render Target being used as a Sprite Texture, or not?
     *
     * @return {this} This Game Object instance.
     */
    setIsSpriteTexture: function (value)
    {
        this.isSpriteTexture = value;

        return this;
    },

    /**
     * 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)
    {
        var camera = this.camera;
        var renderer = this.renderer;

        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 renderTarget = this.renderTarget;

        camera.preRender();

        if (renderTarget)
        {
            renderTarget.bind(true);

            var pipeline = this.pipeline.manager.set(this.pipeline);

            var sx = renderer.width / renderTarget.width;
            var sy = renderer.height / renderTarget.height;

            pipeline.drawFillRect(
                x * sx, y * sy, width * sx, height * sy,
                Utils.getTintFromFloats(b / 255, g / 255, r / 255, 1),
                alpha
            );

            renderTarget.unbind(true);
        }
        else
        {
            var ctx = this.context;

            renderer.setContext(ctx);

            ctx.globalCompositeOperation = 'source-over';
            ctx.fillStyle = 'rgba(' + r + ',' + g + ',' + b + ',' + alpha + ')';
            ctx.fillRect(x, y, width, height);

            renderer.setContext();
        }

        this.dirty = true;

        return this;
    },

    /**
     * Fully clears this Dynamic Texture, erasing everything from it and resetting it back to
     * a blank, transparent, texture.
     *
     * @method Phaser.Textures.DynamicTexture#clear
     * @since 3.2.0
     *
     * @return {this} This Dynamic Texture instance.
     */
    clear: function ()
    {
        if (this.dirty)
        {
            var ctx = this.context;
            var renderTarget = this.renderTarget;

            if (renderTarget)
            {
                renderTarget.clear();
            }
            else if (ctx)
            {
                ctx.save();
                ctx.setTransform(1, 0, 0, 1, 0, 0);
                ctx.clearRect(0, 0, this.width, this.height);
                ctx.restore();
            }

            this.dirty = false;
        }

        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.
     *
     * @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);
        var erase = GetFastValue(config, 'erase', false);

        var stamp = this.manager.resetStamp(alpha, tint);

        stamp.setAngle(0);

        if (angle !== 0)
        {
            stamp.setAngle(angle);
        }
        else if (rotation !== 0)
        {
            stamp.setRotation(rotation);
        }

        stamp.setScale(scaleX, scaleY);
        stamp.setTexture(key, frame);
        stamp.setOrigin(originX, originY);
        stamp.setBlendMode(blendMode);

        if (erase)
        {
            this._eraseMode = true;
        }

        this.draw(stamp, x, y);

        if (erase)
        {
            this._eraseMode = false;
        }

        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.
     *
     * 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: You cannot erase a Dynamic Texture from itself.
     *
     * 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.
     *
     * 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.
     *
     * Calling this method causes the WebGL batch to flush, so it can write the texture
     * data to the framebuffer being used internally. The batch is flushed at the end,
     * after the entries have been iterated. So if you've a bunch of objects to draw,
     * try and pass them in an array in one single call, rather than making lots of
     * separate calls.
     *
     * If you are not planning on using this Dynamic Texture as a base texture for Sprite
     * Game Objects, then you should set `DynamicTexture.isSpriteTexture = false` before
     * calling this method, otherwise you will get vertically inverted frames in WebGL.
     *
     * @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.
     *
     * @return {this} This Dynamic Texture instance.
     */
    erase: function (entries, x, y)
    {
        this._eraseMode = true;

        this.draw(entries, x, y);

        this._eraseMode = 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: For Game Objects that have Post FX Pipelines, the pipeline _cannot_ be
     * used when drawn to this texture.
     *
     * 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.
     *
     * The `alpha` and `tint` values are only used by Texture Frames.
     * Game Objects use their own alpha and tint values when being drawn.
     *
     * Calling this method causes the WebGL batch to flush, so it can write the texture
     * data to the framebuffer being used internally. The batch is flushed at the end,
     * after the entries have been iterated. So if you've a bunch of objects to draw,
     * try and pass them in an array in one single call, rather than making lots of
     * separate calls.
     *
     * If you are not planning on using this Dynamic Texture as a base texture for Sprite
     * Game Objects, then you should set `DynamicTexture.isSpriteTexture = false` before
     * calling this method, otherwise you will get vertically inverted frames in WebGL.
     *
     * @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)
    {
        this.beginDraw();
        this.batchDraw(entries, x, y, alpha, tint);
        this.endDraw();

        return this;
    },

    /**
     * Draws the Texture Frame to the Render Texture at the given position.
     *
     * Textures are referenced by their string-based keys, as stored in the Texture Manager.
     *
     * ```javascript
     * var rt = this.add.renderTexture(0, 0, 800, 600);
     * rt.drawFrame(key, frame);
     * ```
     *
     * You can optionally provide a position, alpha and tint value to apply to the frame
     * before it is drawn.
     *
     * Calling this method will cause a batch flush, so if you've got a stack of things to draw
     * in a tight loop, try using the `draw` method instead.
     *
     * If you need to draw a Sprite to this Render Texture, use the `draw` method instead.
     *
     * If you are not planning on using this Dynamic Texture as a base texture for Sprite
     * Game Objects, then you should set `DynamicTexture.isSpriteTexture = false` before
     * calling this method, otherwise you will get vertically inverted frames in WebGL.
     *
     * @method Phaser.Textures.DynamicTexture#drawFrame
     * @since 3.12.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 {number} [alpha=1] -  The alpha value. Only used when drawing Texture Frames to this texture.
     * @param {number} [tint=0xffffff] -  The tint color value. Only used when drawing Texture Frames to this texture. WebGL only.
     *
     * @return {this} This Dynamic Texture instance.
     */
    drawFrame: function (key, frame, x, y, alpha, tint)
    {
        this.beginDraw();
        this.batchDrawFrame(key, frame, x, y, alpha, tint);
        this.endDraw();

        return this;
    },

    /**
     * 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.
     *
     * 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.
     *
     * Calling this method will cause a batch flush by default. Use the `skipBatch` argument
     * to disable this if this call is part of a larger batch draw.
     *
     * If you are not planning on using this Dynamic Texture as a base texture for Sprite
     * Game Objects, then you should set `DynamicTexture.isSpriteTexture = false` before
     * calling this method, otherwise you will get vertically inverted frames in WebGL.
     *
     * @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 {number} [alpha=1] - The alpha to use. Defaults to 1, no alpha.
     * @param {number} [tint=0xffffff] - WebGL only. The tint color to use. Leave as undefined, or 0xffffff to have no tint.
     * @param {boolean} [skipBatch=false] - Skip beginning and ending a batch with this call. Use if this is part of a bigger batched draw.
     *
     * @return {this} This Dynamic Texture instance.
     */
    repeat: function (key, frame, x, y, width, height, alpha, tint, skipBatch)
    {
        if (x === undefined) { x = 0; }
        if (y === undefined) { y = 0; }
        if (width === undefined) { width = this.width; }
        if (height === undefined) { height = this.height; }
        if (alpha === undefined) { alpha = 1; }
        if (tint === undefined) { tint = 0xffffff; }
        if (skipBatch === undefined) { skipBatch = false; }

        if (key instanceof Frame)
        {
            frame = key;
        }
        else
        {
            frame = this.manager.getFrame(key, frame);
        }

        if (!frame)
        {
            return this;
        }

        var stamp = this.manager.resetStamp(alpha, tint);

        stamp.setFrame(frame);
        stamp.setOrigin(0);

        var frameWidth = frame.width;
        var frameHeight = frame.height;

        //  Clamp to integer
        width = Math.floor(width);
        height = Math.floor(height);

        //  How many stamps can we fit in horizontally and vertically?
        //  We round this number up to allow for excess overflow
        var hmax = Math.ceil(width / frameWidth);
        var vmax = Math.ceil(height / frameHeight);

        //  How much extra horizontal and vertical space do we have on the right/bottom?
        var hdiff = (hmax * frameWidth) - width;
        var vdiff = (vmax * frameHeight) - height;

        if (hdiff > 0)
        {
            hdiff = frameWidth - hdiff;
        }

        if (vdiff > 0)
        {
            vdiff = frameHeight - vdiff;
        }

        //  x/y may be negative

        if (x < 0)
        {
            hmax += Math.ceil(Math.abs(x) / frameWidth);
        }

        if (y < 0)
        {
            vmax += Math.ceil(Math.abs(y) / frameHeight);
        }

        var dx = x;
        var dy = y;

        var useCrop = false;
        var cropRect = this.manager.stampCrop.setTo(0, 0, frameWidth, frameHeight);

        if (!skipBatch)
        {
            this.beginDraw();
        }

        for (var ty = 0; ty < vmax; ty++)
        {
            //  Negative offset?
            if (dy + frameHeight < 0)
            {
                //  We can't see it, as it's off the top
                dy += frameHeight;
                continue;
            }

            for (var tx = 0; tx < hmax; tx++)
            {
                useCrop = false;

                //  Negative offset?
                if (dx + frameWidth < 0)
                {
                    //  We can't see it, as it's fully off the left
                    dx += frameWidth;
                    continue;
                }
                else if (dx < 0)
                {
                    //  Partially off the left
                    useCrop = true;
                    cropRect.width = (frameWidth + dx);
                    cropRect.x = frameWidth - cropRect.width;
                }

                //  Negative vertical offset
                if (dy < 0)
                {
                    //  Partially off the top
                    useCrop = true;
                    cropRect.height = (frameHeight + dy);
                    cropRect.y = frameHeight - cropRect.height;
                }

                if (hdiff > 0 && tx === hmax - 1)
                {
                    useCrop = true;
                    cropRect.width = hdiff;
                }

                if (vdiff > 0 && ty === vmax - 1)
                {
                    useCrop = true;
                    cropRect.height = vdiff;
                }

                if (useCrop)
                {
                    stamp.setCrop(cropRect);
                }

                this.batchGameObject(stamp, dx, dy);

                //  Reset crop
                stamp.isCropped = false;

                cropRect.setTo(0, 0, frameWidth, frameHeight);

                dx += frameWidth;
            }

            dx = x;
            dy += frameHeight;
        }

        if (!skipBatch)
        {
            this.endDraw();
        }

        return this;
    },

    /**
     * Use this method if you need to batch draw a large number of Game Objects to
     * this Dynamic Texture in a single pass, or on a frequent basis. This is especially
     * useful under WebGL, however, if your game is using Canvas only, it will not make
     * any speed difference in that situation.
     *
     * This method starts the beginning of a batched draw, unless one is already open.
     *
     * Batched drawing is faster than calling `draw` in loop, but you must be careful
     * to manage the flow of code and remember to call `endDraw()` when you're finished.
     *
     * If you don't need to draw large numbers of objects it's much safer and easier
     * to use the `draw` method instead.
     *
     * The flow should be:
     *
     * ```javascript
     * // Call once:
     * DynamicTexture.beginDraw();
     *
     * // repeat n times:
     * DynamicTexture.batchDraw();
     * // or
     * DynamicTexture.batchDrawFrame();
     *
     * // Call once:
     * DynamicTexture.endDraw();
     * ```
     *
     * Do not call any methods other than `batchDraw`, `batchDrawFrame`, or `endDraw` once you
     * have started a batch. Also, be very careful not to destroy this Dynamic Texture while the
     * batch is still open. Doing so will cause a run-time error in the WebGL Renderer.
     *
     * You can use the `DynamicTexture.isDrawing` boolean property to tell if a batch is
     * currently open, or not.
     *
     * @method Phaser.Textures.DynamicTexture#beginDraw
     * @since 3.50.0
     *
     * @return {this} This Dynamic Texture instance.
     */
    beginDraw: function ()
    {
        if (!this.isDrawing)
        {
            var camera = this.camera;
            var renderer = this.renderer;
            var renderTarget = this.renderTarget;

            camera.preRender();

            if (renderTarget)
            {
                renderer.beginCapture(renderTarget.width, renderTarget.height);
            }
            else
            {
                renderer.setContext(this.context);
            }

            this.isDrawing = true;
        }

        return this;
    },

    /**
     * Use this method if you have already called `beginDraw` and need to batch
     * draw a large number of objects to this Dynamic Texture.
     *
     * This method batches the drawing of the given objects to this texture,
     * without causing a WebGL bind or batch flush for each one.
     *
     * It is faster than calling `draw`, but you must be careful to manage the
     * flow of code and remember to call `endDraw()`. If you don't need to draw large
     * numbers of objects it's much safer and easier to use the `draw` method instead.
     *
     * The flow should be:
     *
     * ```javascript
     * // Call once:
     * DynamicTexture.beginDraw();
     *
     * // repeat n times:
     * DynamicTexture.batchDraw();
     * // or
     * DynamicTexture.batchDrawFrame();
     *
     * // Call once:
     * DynamicTexture.endDraw();
     * ```
     *
     * Do not call any methods other than `batchDraw`, `batchDrawFrame`, or `endDraw` once you
     * have started a batch. Also, be very careful not to destroy this Dynamic Texture while the
     * batch is still open. Doing so will cause a run-time error in the WebGL Renderer.
     *
     * You can use the `DynamicTexture.isDrawing` boolean property to tell if a batch is
     * currently open, or not.
     *
     * This method 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's Display List. Pass in `Scene.children` to draw the whole list.
     * * Another Dynamic Texture or Render Texture.
     * * A Texture Frame instance.
     * * A string. This is used to look-up a texture from the Texture Manager.
     *
     * Note: You cannot draw a Dynamic Texture to itself.
     *
     * 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.
     *
     * 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#batchDraw
     * @since 3.50.0
     *
     * @param {any} entries - Any renderable Game Object, or Group, Container, Display List, other Dynamic or 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.
     */
    batchDraw: function (entries, x, y, alpha, tint)
    {
        if (!Array.isArray(entries))
        {
            entries = [ entries ];
        }

        this.batchList(entries, x, y, alpha, tint);

        return this;
    },

    /**
     * Use this method if you have already called `beginDraw` and need to batch
     * draw a large number of texture frames to this Dynamic Texture.
     *
     * This method batches the drawing of the given frames to this Dynamic Texture,
     * without causing a WebGL bind or batch flush for each one.
     *
     * It is faster than calling `drawFrame`, but you must be careful to manage the
     * flow of code and remember to call `endDraw()`. If you don't need to draw large
     * numbers of frames it's much safer and easier to use the `drawFrame` method instead.
     *
     * The flow should be:
     *
     * ```javascript
     * // Call once:
     * DynamicTexture.beginDraw();
     *
     * // repeat n times:
     * DynamicTexture.batchDraw();
     * // or
     * DynamicTexture.batchDrawFrame();
     *
     * // Call once:
     * DynamicTexture.endDraw();
     * ```
     *
     * Do not call any methods other than `batchDraw`, `batchDrawFrame`, or `endDraw` once you
     * have started a batch. Also, be very careful not to destroy this Dynamic Texture while the
     * batch is still open. Doing so will cause a run-time error in the WebGL Renderer.
     *
     * You can use the `DynamicTexture.isDrawing` boolean property to tell if a batch is
     * currently open, or not.
     *
     * Textures are referenced by their string-based keys, as stored in the Texture Manager.
     *
     * You can optionally provide a position, alpha and tint value to apply to the frame
     * before it is drawn.
     *
     * @method Phaser.Textures.DynamicTexture#batchDrawFrame
     * @since 3.50.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.
     * @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 {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.
     */
    batchDrawFrame: function (key, frame, x, y, alpha, tint)
    {
        if (x === undefined) { x = 0; }
        if (y === undefined) { y = 0; }
        if (alpha === undefined) { alpha = 1; }
        if (tint === undefined) { tint = 0xffffff; }

        var textureFrame = this.manager.getFrame(key, frame);

        if (textureFrame)
        {
            if (this.renderTarget)
            {
                this.pipeline.batchTextureFrame(textureFrame, x, y, tint, alpha, this.camera.matrix, null);
            }
            else
            {
                this.batchTextureFrame(textureFrame, x, y, alpha, tint);
            }
        }

        return this;
    },

    /**
     * Use this method to finish batch drawing to this Dynamic Texture.
     *
     * Doing so will stop the WebGL Renderer from capturing draws and then blit the
     * framebuffer to the Render Target owned by this texture.
     *
     * Calling this method without first calling `beginDraw` will have no effect.
     *
     * Batch drawing is faster than calling `draw`, but you must be careful to manage the
     * flow of code and remember to call `endDraw()` when you're finished.
     *
     * If you don't need to draw large numbers of objects it's much safer and easier
     * to use the `draw` method instead.
     *
     * The flow should be:
     *
     * ```javascript
     * // Call once:
     * DynamicTexture.beginDraw();
     *
     * // repeat n times:
     * DynamicTexture.batchDraw();
     * // or
     * DynamicTexture.batchDrawFrame();
     *
     * // Call once:
     * DynamicTexture.endDraw();
     * ```
     *
     * Do not call any methods other than `batchDraw`, `batchDrawFrame`, or `endDraw` once you
     * have started a batch. Also, be very careful not to destroy this Dynamic Texture while the
     * batch is still open. Doing so will cause a run-time error in the WebGL Renderer.
     *
     * You can use the `DynamicTexture.isDrawing` boolean property to tell if a batch is
     * currently open, or not.
     *
     * @method Phaser.Textures.DynamicTexture#endDraw
     * @since 3.50.0
     *
     * @param {boolean} [erase=false] - Draws all objects in this batch using a blend mode of ERASE. This has the effect of erasing any filled pixels in the objects being drawn.
     *
     * @return {this} This Dynamic Texture instance.
     */
    endDraw: function (erase)
    {
        if (erase === undefined) { erase = this._eraseMode; }

        if (this.isDrawing)
        {
            var renderer = this.renderer;

            var renderTarget = this.renderTarget;

            if (renderTarget)
            {
                var canvasTarget = renderer.endCapture();

                var util = renderer.pipelines.setUtility();

                util.blitFrame(canvasTarget, renderTarget, 1, false, false, erase, this.isSpriteTexture);

                renderer.resetScissor();
                renderer.resetViewport();
            }
            else
            {
                renderer.setContext();
            }

            this.dirty = true;
            this.isDrawing = false;
        }

        return this;
    },

    /**
     * Internal method that handles the drawing of an array of children.
     *
     * @method Phaser.Textures.DynamicTexture#batchList
     * @private
     * @since 3.12.0
     *
     * @param {array} children - The array of Game Objects, Textures or Frames to draw.
     * @param {number} [x=0] - The x position to offset the Game Object by.
     * @param {number} [y=0] - The y position to offset the Game Object by.
     * @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.
     */
    batchList: function (children, x, y, alpha, tint)
    {
        var len = children.length;

        if (len === 0)
        {
            return;
        }

        for (var i = 0; i < len; i++)
        {
            var entry = children[i];

            if (!entry || entry === this)
            {
                continue;
            }

            if (entry.renderWebGL || entry.renderCanvas)
            {
                //  Game Objects
                this.batchGameObject(entry, x, y);
            }
            else if (entry.isParent || entry.list)
            {
                //  Groups / Display Lists
                this.batchGroup(entry.getChildren(), x, y);
            }
            else if (typeof entry === 'string')
            {
                //  Texture key
                this.batchTextureFrameKey(entry, null, x, y, alpha, tint);
            }
            else if (entry instanceof Frame)
            {
                //  Texture Frame instance
                this.batchTextureFrame(entry, x, y, alpha, tint);
            }
            else if (Array.isArray(entry))
            {
                //  Another Array
                this.batchList(entry, x, y, alpha, tint);
            }
        }
    },

    /**
     * Internal method that handles drawing the contents of a Phaser Group to this Dynamic Texture.
     *
     * @method Phaser.Textures.DynamicTexture#batchGroup
     * @private
     * @since 3.12.0
     *
     * @param {array} children - The array of Game Objects to draw.
     * @param {number} [x=0] - The x position to offset the Game Objects by.
     * @param {number} [y=0] - The y position to offset the Game Objects by.
     */
    batchGroup: function (children, x, y)
    {
        if (x === undefined) { x = 0; }
        if (y === undefined) { y = 0; }

        for (var i = 0; i < children.length; i++)
        {
            var entry = children[i];

            if (entry.willRender(this.camera))
            {
                this.batchGameObject(entry, entry.x + x, entry.y + y);
            }
        }
    },

    /**
     * Internal method that handles drawing a single Phaser Game Object to this Dynamic Texture.
     *
     * @method Phaser.Textures.DynamicTexture#batchGameObject
     * @private
     * @since 3.12.0
     *
     * @param {Phaser.GameObjects.GameObject} gameObject - The Game Object to draw.
     * @param {number} [x=0] - The x position to draw the Game Object at.
     * @param {number} [y=0] - The y position to draw the Game Object at.
     */
    batchGameObject: function (gameObject, x, y)
    {
        if (x === undefined) { x = gameObject.x; }
        if (y === undefined) { y = gameObject.y; }

        var prevX = gameObject.x;
        var prevY = gameObject.y;

        var camera = this.camera;
        var renderer = this.renderer;
        var eraseMode = this._eraseMode;

        var mask = gameObject.mask;

        gameObject.setPosition(x, y);

        if (this.canvas)
        {
            if (eraseMode)
            {
                var blendMode = gameObject.blendMode;

                gameObject.blendMode = BlendModes.ERASE;
            }

            if (mask)
            {
                mask.preRenderCanvas(renderer, gameObject, camera);
            }

            gameObject.renderCanvas(renderer, gameObject, camera, null);

            if (mask)
            {
                mask.postRenderCanvas(renderer, gameObject, camera);
            }

            if (eraseMode)
            {
                gameObject.blendMode = blendMode;
            }
        }
        else if (renderer)
        {
            if (mask)
            {
                mask.preRenderWebGL(renderer, gameObject, camera);
            }

            if (!eraseMode)
            {
                renderer.setBlendMode(gameObject.blendMode);
            }

            gameObject.renderWebGL(renderer, gameObject, camera);

            if (mask)
            {
                mask.postRenderWebGL(renderer, camera, this.renderTarget);
            }
        }

        gameObject.setPosition(prevX, prevY);
    },

    /**
     * Internal method that handles the drawing a Texture Frame based on its key.
     *
     * @method Phaser.Textures.DynamicTexture#batchTextureFrameKey
     * @private
     * @since 3.12.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.
     * @param {number} [x=0] - The x position to offset the Game Object by.
     * @param {number} [y=0] - The y position to offset the Game Object by.
     * @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.
     */
    batchTextureFrameKey: function (key, frame, x, y, alpha, tint)
    {
        var textureFrame = this.manager.getFrame(key, frame);

        if (textureFrame)
        {
            this.batchTextureFrame(textureFrame, x, y, alpha, tint);
        }
    },

    /**
     * Internal method that handles the drawing of a Texture Frame to this Dynamic Texture.
     *
     * @method Phaser.Textures.DynamicTexture#batchTextureFrame
     * @private
     * @since 3.12.0
     *
     * @param {Phaser.Textures.Frame} textureFrame - The Texture Frame to draw.
     * @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 {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.
     */
    batchTextureFrame: function (textureFrame, x, y, alpha, tint)
    {
        if (x === undefined) { x = 0; }
        if (y === undefined) { y = 0; }
        if (alpha === undefined) { alpha = 1; }
        if (tint === undefined) { tint = 0xffffff; }

        var matrix = this.camera.matrix;
        var renderTarget = this.renderTarget;

        if (renderTarget)
        {
            this.pipeline.batchTextureFrame(textureFrame, x, y, tint, alpha, matrix, null);
        }
        else
        {
            var ctx = this.context;
            var cd = textureFrame.canvasData;
            var source = textureFrame.source.image;

            ctx.save();

            ctx.globalCompositeOperation = (this._eraseMode) ? 'destination-out' : 'source-over';

            ctx.globalAlpha = alpha;

            matrix.setToContext(ctx);

            if (cd.width > 0 && cd.height > 0)
            {
                ctx.drawImage(source, cd.x, cd.y, cd.width, cd.height, x, y, cd.width, cd.height);
            }

            ctx.restore();
        }
    },

    /**
     * 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.renderTarget)
        {
            this.renderer.snapshotFramebuffer(this.renderTarget.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');
    },

    /**
     * Renders this Dynamic Texture onto the Stamp Game Object as a BitmapMask.
     *
     * @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.renderWebGL(renderer, stamp, camera, parentMatrix);
    },

    /**
     * This is a NOOP method. Bitmap Masks are not supported by the Canvas Renderer.
     *
     * @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
    },

    /**
     * Internal destroy handler, called as part of the destroy process.
     *
     * @method Phaser.Textures.DynamicTexture#preDestroy
     * @protected
     * @since 3.9.0
     */
    preDestroy: function ()
    {
        CanvasPool.remove(this.canvas);

        if (this.renderTarget)
        {
            this.renderTarget.destroy();
        }

        this.camera.destroy();
        this.stamp.destroy();

        this.canvas = null;
        this.context = null;
        this.renderer = null;
        this.scene = null;
    }

});

module.exports = DynamicTexture;


/**
* The `Matter.Collision` module contains methods for detecting collisions between a given pair of bodies.
*
* For efficient detection between a list of bodies, see `Matter.Detector` and `Matter.Query`.
*
* See `Matter.Engine` for collision events.
*
* @class Collision
*/

var Collision = {};

module.exports = Collision;

var Vertices = require('../geometry/Vertices');
var Pair = require('./Pair');

(function() {
    var _supports = [];

    var _overlapAB = {
        overlap: 0,
        axis: null
    };

    var _overlapBA = {
        overlap: 0,
        axis: null
    };

    /**
     * Creates a new collision record.
     * @method create
     * @param {body} bodyA The first body part represented by the collision record
     * @param {body} bodyB The second body part represented by the collision record
     * @return {collision} A new collision record
     */
    Collision.create = function(bodyA, bodyB) {
        return {
            pair: null,
            collided: false,
            bodyA: bodyA,
            bodyB: bodyB,
            parentA: bodyA.parent,
            parentB: bodyB.parent,
            depth: 0,
            normal: { x: 0, y: 0 },
            tangent: { x: 0, y: 0 },
            penetration: { x: 0, y: 0 },
            supports: []
        };
    };

    /**
     * Detect collision between two bodies.
     * @method collides
     * @param {body} bodyA
     * @param {body} bodyB
     * @param {pairs} [pairs] Optionally reuse collision records from existing pairs.
     * @return {collision|null} A collision record if detected, otherwise null
     */
    Collision.collides = function(bodyA, bodyB, pairs) {
        Collision._overlapAxes(_overlapAB, bodyA.vertices, bodyB.vertices, bodyA.axes);

        if (_overlapAB.overlap <= 0) {
            return null;
        }

        Collision._overlapAxes(_overlapBA, bodyB.vertices, bodyA.vertices, bodyB.axes);

        if (_overlapBA.overlap <= 0) {
            return null;
        }

        // reuse collision records for gc efficiency
        var pair = pairs && pairs.table[Pair.id(bodyA, bodyB)],
            collision;

        if (!pair) {
            collision = Collision.create(bodyA, bodyB);
            collision.collided = true;
            collision.bodyA = bodyA.id < bodyB.id ? bodyA : bodyB;
            collision.bodyB = bodyA.id < bodyB.id ? bodyB : bodyA;
            collision.parentA = collision.bodyA.parent;
            collision.parentB = collision.bodyB.parent;
        } else {
            collision = pair.collision;
        }

        bodyA = collision.bodyA;
        bodyB = collision.bodyB;

        var minOverlap;

        if (_overlapAB.overlap < _overlapBA.overlap) {
            minOverlap = _overlapAB;
        } else {
            minOverlap = _overlapBA;
        }

        var normal = collision.normal,
            supports = collision.supports,
            minAxis = minOverlap.axis,
            minAxisX = minAxis.x,
            minAxisY = minAxis.y;

        // ensure normal is facing away from bodyA
        if (minAxisX * (bodyB.position.x - bodyA.position.x) + minAxisY * (bodyB.position.y - bodyA.position.y) < 0) {
            normal.x = minAxisX;
            normal.y = minAxisY;
        } else {
            normal.x = -minAxisX;
            normal.y = -minAxisY;
        }

        collision.tangent.x = -normal.y;
        collision.tangent.y = normal.x;

        collision.depth = minOverlap.overlap;

        collision.penetration.x = normal.x * collision.depth;
        collision.penetration.y = normal.y * collision.depth;

        // find support points, there is always either exactly one or two
        var supportsB = Collision._findSupports(bodyA, bodyB, normal, 1),
            supportCount = 0;

        // find the supports from bodyB that are inside bodyA
        if (Vertices.contains(bodyA.vertices, supportsB[0])) {
            supports[supportCount++] = supportsB[0];
        }

        if (Vertices.contains(bodyA.vertices, supportsB[1])) {
            supports[supportCount++] = supportsB[1];
        }

        // find the supports from bodyA that are inside bodyB
        if (supportCount < 2) {
            var supportsA = Collision._findSupports(bodyB, bodyA, normal, -1);

            if (Vertices.contains(bodyB.vertices, supportsA[0])) {
                supports[supportCount++] = supportsA[0];
            }

            if (supportCount < 2 && Vertices.contains(bodyB.vertices, supportsA[1])) {
                supports[supportCount++] = supportsA[1];
            }
        }

        // account for the edge case of overlapping but no vertex containment
        if (supportCount === 0) {
            supports[supportCount++] = supportsB[0];
        }

        // update supports array size
        supports.length = supportCount;

        return collision;
    };

    /**
     * Find the overlap between two sets of vertices.
     * @method _overlapAxes
     * @private
     * @param {object} result
     * @param {vertices} verticesA
     * @param {vertices} verticesB
     * @param {axes} axes
     */
    Collision._overlapAxes = function(result, verticesA, verticesB, axes) {
        var verticesALength = verticesA.length,
            verticesBLength = verticesB.length,
            verticesAX = verticesA[0].x,
            verticesAY = verticesA[0].y,
            verticesBX = verticesB[0].x,
            verticesBY = verticesB[0].y,
            axesLength = axes.length,
            overlapMin = Number.MAX_VALUE,
            overlapAxisNumber = 0,
            overlap,
            overlapAB,
            overlapBA,
            dot,
            i,
            j;

        for (i = 0; i < axesLength; i++) {
            var axis = axes[i],
                axisX = axis.x,
                axisY = axis.y,
                minA = verticesAX * axisX + verticesAY * axisY,
                minB = verticesBX * axisX + verticesBY * axisY,
                maxA = minA,
                maxB = minB;

            for (j = 1; j < verticesALength; j += 1) {
                dot = verticesA[j].x * axisX + verticesA[j].y * axisY;

                if (dot > maxA) {
                    maxA = dot;
                } else if (dot < minA) {
                    minA = dot;
                }
            }

            for (j = 1; j < verticesBLength; j += 1) {
                dot = verticesB[j].x * axisX + verticesB[j].y * axisY;

                if (dot > maxB) {
                    maxB = dot;
                } else if (dot < minB) {
                    minB = dot;
                }
            }

            overlapAB = maxA - minB;
            overlapBA = maxB - minA;
            overlap = overlapAB < overlapBA ? overlapAB : overlapBA;

            if (overlap < overlapMin) {
                overlapMin = overlap;
                overlapAxisNumber = i;

                if (overlap <= 0) {
                    // can not be intersecting
                    break;
                }
            }
        }

        result.axis = axes[overlapAxisNumber];
        result.overlap = overlapMin;
    };

    /**
     * Projects vertices on an axis and returns an interval.
     * @method _projectToAxis
     * @private
     * @param {} projection
     * @param {} vertices
     * @param {} axis
     */
    Collision._projectToAxis = function(projection, vertices, axis) {
        var min = vertices[0].x * axis.x + vertices[0].y * axis.y,
            max = min;

        for (var i = 1; i < vertices.length; i += 1) {
            var dot = vertices[i].x * axis.x + vertices[i].y * axis.y;

            if (dot > max) {
                max = dot;
            } else if (dot < min) {
                min = dot;
            }
        }

        projection.min = min;
        projection.max = max;
    };

    /**
     * Finds supporting vertices given two bodies along a given direction using hill-climbing.
     * @method _findSupports
     * @private
     * @param {body} bodyA
     * @param {body} bodyB
     * @param {vector} normal
     * @param {number} direction
     * @return [vector]
     */
    Collision._findSupports = function(bodyA, bodyB, normal, direction) {
        var vertices = bodyB.vertices,
            verticesLength = vertices.length,
            bodyAPositionX = bodyA.position.x,
            bodyAPositionY = bodyA.position.y,
            normalX = normal.x * direction,
            normalY = normal.y * direction,
            nearestDistance = Number.MAX_VALUE,
            vertexA,
            vertexB,
            vertexC,
            distance,
            j;

        // find deepest vertex relative to the axis
        for (j = 0; j < verticesLength; j += 1) {
            vertexB = vertices[j];
            distance = normalX * (bodyAPositionX - vertexB.x) + normalY * (bodyAPositionY - vertexB.y);

            // convex hill-climbing
            if (distance < nearestDistance) {
                nearestDistance = distance;
                vertexA = vertexB;
            }
        }

        // measure next vertex
        vertexC = vertices[(verticesLength + vertexA.index - 1) % verticesLength];
        nearestDistance = normalX * (bodyAPositionX - vertexC.x) + normalY * (bodyAPositionY - vertexC.y);

        // compare with previous vertex
        vertexB = vertices[(vertexA.index + 1) % verticesLength];
        if (normalX * (bodyAPositionX - vertexB.x) + normalY * (bodyAPositionY - vertexB.y) < nearestDistance) {
            _supports[0] = vertexA;
            _supports[1] = vertexB;

            return _supports;
        }

        _supports[0] = vertexA;
        _supports[1] = vertexC;

        return _supports;
    };

    /*
    *
    *  Properties Documentation
    *
    */

    /**
     * A reference to the pair using this collision record, if there is one.
     *
     * @property pair
     * @type {pair|null}
     * @default null
     */

    /**
     * A flag that indicates if the bodies were colliding when the collision was last updated.
     *
     * @property collided
     * @type boolean
     * @default false
     */

    /**
     * The first body part represented by the collision (see also `collision.parentA`).
     *
     * @property bodyA
     * @type body
     */

    /**
     * The second body part represented by the collision (see also `collision.parentB`).
     *
     * @property bodyB
     * @type body
     */

    /**
     * The first body represented by the collision (i.e. `collision.bodyA.parent`).
     *
     * @property parentA
     * @type body
     */

    /**
     * The second body represented by the collision (i.e. `collision.bodyB.parent`).
     *
     * @property parentB
     * @type body
     */

    /**
     * A `Number` that represents the minimum separating distance between the bodies along the collision normal.
     *
     * @readOnly
     * @property depth
     * @type number
     * @default 0
     */

    /**
     * A normalised `Vector` that represents the direction between the bodies that provides the minimum separating distance.
     *
     * @property normal
     * @type vector
     * @default { x: 0, y: 0 }
     */

    /**
     * A normalised `Vector` that is the tangent direction to the collision normal.
     *
     * @property tangent
     * @type vector
     * @default { x: 0, y: 0 }
     */

    /**
     * A `Vector` that represents the direction and depth of the collision.
     *
     * @property penetration
     * @type vector
     * @default { x: 0, y: 0 }
     */

    /**
     * An array of body vertices that represent the support points in the collision.
     * These are the deepest vertices (along the collision normal) of each body that are contained by the other body's vertices.
     *
     * @property supports
     * @type vector[]
     * @default []
     */

})();