keep keep keep replace replace keep keep keep keep

/**
 * @author       Richard Davey <rich@phaser.io>
 * @copyright    2013-2024 Phaser Studio Inc.
 * @license      {@link https://opensource.org/licenses/MIT|MIT License}
 */

var Class = require('../../utils/Class');
var Components = require('../components');
var GameObject = require('../GameObject');
var GetFastValue = require('../../utils/object/GetFastValue');
var Extend = require('../../utils/object/Extend');
var SetValue = require('../../utils/object/SetValue');
var ShaderRender = require('./ShaderRender');
var TransformMatrix = require('../components/TransformMatrix');
var ArrayEach = require('../../utils/array/Each');
var RenderEvents = require('../../renderer/events');

/**
 * @classdesc
 * A Shader Game Object.
 *
 * This Game Object allows you to easily add a quad with its own shader into the display list, and manipulate it
 * as you would any other Game Object, including scaling, rotating, positioning and adding to Containers. Shaders
 * can be masked with either Bitmap or Geometry masks and can also be used as a Bitmap Mask for a Camera or other
 * Game Object. They can also be made interactive and used for input events.
 *
 * It works by taking a reference to a `Phaser.Display.BaseShader` instance, as found in the Shader Cache. These can
 * be created dynamically at runtime, or loaded in via the GLSL File Loader:
 *
 * ```javascript
 * function preload ()
 * {
 *     this.load.glsl('fire', 'shaders/fire.glsl.js');
 * }
 *
 * function create ()
 * {
 *     this.add.shader('fire', 400, 300, 512, 512);
 * }
 * ```
 *
 * Please see the Phaser 3 Examples GitHub repo for examples of loading and creating shaders dynamically.
 *
 * Due to the way in which they work, you cannot directly change the alpha or blend mode of a Shader. This should
 * be handled via exposed uniforms in the shader code itself.
 *
 * By default a Shader will be created with a standard set of uniforms. These were added to match those
 * found on sites such as ShaderToy or GLSLSandbox, and provide common functionality a shader may need,
 * such as the timestamp, resolution or pointer position. You can replace them by specifying your own uniforms
 * in the Base Shader.
 *
 * These Shaders work by halting the current pipeline during rendering, creating a viewport matched to the
 * size of this Game Object and then renders a quad using the bound shader. At the end, the pipeline is restored.
 *
 * Because it blocks the pipeline it means it will interrupt any batching that is currently going on, so you should
 * use these Game Objects sparingly. If you need to have a fully batched custom shader, then please look at using
 * a custom pipeline instead. However, for background or special masking effects, they are extremely effective.
 *
 * @class Shader
 * @extends Phaser.GameObjects.GameObject
 * @memberof Phaser.GameObjects
 * @constructor
 * @webglOnly
 * @since 3.17.0
 *
 * @extends Phaser.GameObjects.Components.ComputedSize
 * @extends Phaser.GameObjects.Components.Depth
 * @extends Phaser.GameObjects.Components.GetBounds
 * @extends Phaser.GameObjects.Components.Mask
 * @extends Phaser.GameObjects.Components.Origin
 * @extends Phaser.GameObjects.Components.ScrollFactor
 * @extends Phaser.GameObjects.Components.Transform
 * @extends Phaser.GameObjects.Components.Visible
 *
 * @param {Phaser.Scene} scene - The Scene to which this Game Object belongs. A Game Object can only belong to one Scene at a time.
 * @param {(string|Phaser.Display.BaseShader)} key - The key of the shader to use from the shader cache, or a BaseShader instance.
 * @param {number} [x=0] - The horizontal position of this Game Object in the world.
 * @param {number} [y=0] - The vertical position of this Game Object in the world.
 * @param {number} [width=128] - The width of the Game Object.
 * @param {number} [height=128] - The height of the Game Object.
 * @param {string[]} [textures] - Optional array of texture keys to bind to the iChannel0...3 uniforms. The textures must already exist in the Texture Manager.
 * @param {any} [textureData] - Additional texture data if you want to create shader with none NPOT textures.
 */
var Shader = new Class({

    Extends: GameObject,

    Mixins: [
        Components.ComputedSize,
        Components.Depth,
        Components.GetBounds,
        Components.Mask,
        Components.Origin,
        Components.ScrollFactor,
        Components.Transform,
        Components.Visible,
        ShaderRender
    ],

    initialize:

    function Shader (scene, key, x, y, width, height, textures, textureData)
    {
        if (x === undefined) { x = 0; }
        if (y === undefined) { y = 0; }
        if (width === undefined) { width = 128; }
        if (height === undefined) { height = 128; }

        GameObject.call(this, scene, 'Shader');

        /**
         * This Game Object cannot have a blend mode, so skip all checks.
         *
         * @name Phaser.GameObjects.Shader#blendMode
         * @type {number}
         * @private
         * @since 3.17.0
         */
        this.blendMode = -1;

        /**
         * The underlying shader object being used.
         * Empty by default and set during a call to the `setShader` method.
         *
         * @name Phaser.GameObjects.Shader#shader
         * @type {Phaser.Display.BaseShader}
         * @since 3.17.0
         */
        this.shader;

        var renderer = scene.sys.renderer;

        /**
         * A reference to the current renderer.
         * Shaders only work with the WebGL Renderer.
         *
         * @name Phaser.GameObjects.Shader#renderer
         * @type {(Phaser.Renderer.Canvas.CanvasRenderer|Phaser.Renderer.WebGL.WebGLRenderer)}
         * @since 3.17.0
         */
        this.renderer = renderer;

        /**
         * The WebGL context belonging to the renderer.
         *
         * @name Phaser.GameObjects.Shader#gl
         * @type {WebGLRenderingContext}
         * @since 3.17.0
         */
        this.gl = renderer.gl;

        /**
         * Raw byte buffer of vertices this Shader uses.
         *
         * @name Phaser.GameObjects.Shader#vertexData
         * @type {ArrayBuffer}
         * @since 3.17.0
         */
        this.vertexData = new ArrayBuffer(6 * (Float32Array.BYTES_PER_ELEMENT * 2));

        /**
         * The WebGL vertex buffer object this shader uses.
         *
         * @name Phaser.GameObjects.Shader#vertexBuffer
         * @type {Phaser.Renderer.WebGL.Wrappers.WebGLBufferWrapper}
         * @since 3.17.0
         */
        this.vertexBuffer = renderer.createVertexBuffer(this.vertexData.byteLength, this.gl.STREAM_DRAW);

        /**
         * Internal property: whether the shader needs to be created,
         * and if so, the key and textures to use for the shader.
         *
         * @name Phaser.GameObjects.Shader#_deferSetShader
         * @type {?{ key: string, textures: string[]|undefined, textureData: any|undefined }}
         * @private
         * @since 3.80.0
         */
        this._deferSetShader = null;

        /**
         * Internal property: whether the projection matrix needs to be set,
         * and if so, the data to use for the orthographic projection.
         *
         * @name Phaser.GameObjects.Shader#_deferProjOrtho
         * @type {?{ left: number, right: number, bottom: number, top: number }}
         * @private
         * @since 3.80.0
         */
        this._deferProjOrtho = null;

        /**
         * The WebGL shader program this shader uses.
         *
         * @name Phaser.GameObjects.Shader#program
         * @type {Phaser.Renderer.WebGL.Wrappers.WebGLProgramWrapper}
         * @since 3.17.0
         */
        this.program = null;

        /**
         * Uint8 view to the vertex raw buffer. Used for uploading vertex buffer resources to the GPU.
         *
         * @name Phaser.GameObjects.Shader#bytes
         * @type {Uint8Array}
         * @since 3.17.0
         */
        this.bytes = new Uint8Array(this.vertexData);

        /**
         * Float32 view of the array buffer containing the shaders vertices.
         *
         * @name Phaser.GameObjects.Shader#vertexViewF32
         * @type {Float32Array}
         * @since 3.17.0
         */
        this.vertexViewF32 = new Float32Array(this.vertexData);

        /**
         * A temporary Transform Matrix, re-used internally during batching.
         *
         * @name Phaser.GameObjects.Shader#_tempMatrix1
         * @private
         * @type {Phaser.GameObjects.Components.TransformMatrix}
         * @since 3.17.0
         */
        this._tempMatrix1 = new TransformMatrix();

        /**
         * A temporary Transform Matrix, re-used internally during batching.
         *
         * @name Phaser.GameObjects.Shader#_tempMatrix2
         * @private
         * @type {Phaser.GameObjects.Components.TransformMatrix}
         * @since 3.17.0
         */
        this._tempMatrix2 = new TransformMatrix();

        /**
         * A temporary Transform Matrix, re-used internally during batching.
         *
         * @name Phaser.GameObjects.Shader#_tempMatrix3
         * @private
         * @type {Phaser.GameObjects.Components.TransformMatrix}
         * @since 3.17.0
         */
        this._tempMatrix3 = new TransformMatrix();

        /**
         * The view matrix the shader uses during rendering.
         *
         * @name Phaser.GameObjects.Shader#viewMatrix
         * @type {Float32Array}
         * @readonly
         * @since 3.17.0
         */
        this.viewMatrix = new Float32Array([ 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1 ]);

        /**
         * The projection matrix the shader uses during rendering.
         *
         * @name Phaser.GameObjects.Shader#projectionMatrix
         * @type {Float32Array}
         * @readonly
         * @since 3.17.0
         */
        this.projectionMatrix = new Float32Array([ 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1 ]);

        /**
         * The default uniform mappings. These can be added to (or replaced) by specifying your own uniforms when
         * creating this shader game object. The uniforms are updated automatically during the render step.
         *
         * The defaults are:
         *
         * `resolution` (2f) - Set to the size of this shader.
         * `time` (1f) - The elapsed game time, in seconds.
         * `mouse` (2f) - If a pointer has been bound (with `setPointer`), this uniform contains its position each frame.
         * `date` (4fv) - A vec4 containing the year, month, day and time in seconds.
         * `sampleRate` (1f) - Sound sample rate. 44100 by default.
         * `iChannel0...3` (sampler2D) - Input channels 0 to 3. `null` by default.
         *
         * @name Phaser.GameObjects.Shader#uniforms
         * @type {any}
         * @since 3.17.0
         */
        this.uniforms = {};

        /**
         * The pointer bound to this shader, if any.
         * Set via the chainable `setPointer` method, or by modifying this property directly.
         *
         * @name Phaser.GameObjects.Shader#pointer
         * @type {Phaser.Input.Pointer}
         * @since 3.17.0
         */
        this.pointer = null;

        /**
         * The cached width of the renderer.
         *
         * @name Phaser.GameObjects.Shader#_rendererWidth
         * @type {number}
         * @private
         * @since 3.17.0
         */
        this._rendererWidth = renderer.width;

        /**
         * The cached height of the renderer.
         *
         * @name Phaser.GameObjects.Shader#_rendererHeight
         * @type {number}
         * @private
         * @since 3.17.0
         */
        this._rendererHeight = renderer.height;

        /**
         * Internal texture count tracker.
         *
         * @name Phaser.GameObjects.Shader#_textureCount
         * @type {number}
         * @private
         * @since 3.17.0
         */
        this._textureCount = 0;

        /**
         * A reference to the GL Frame Buffer this Shader is drawing to.
         * This property is only set if you have called `Shader.setRenderToTexture`.
         *
         * @name Phaser.GameObjects.Shader#framebuffer
         * @type {?Phaser.Renderer.WebGL.Wrappers.WebGLFramebufferWrapper}
         * @since 3.19.0
         */
        this.framebuffer = null;

        /**
         * A reference to the WebGLTextureWrapper this Shader is rendering to.
         * This property is only set if you have called `Shader.setRenderToTexture`.
         *
         * @name Phaser.GameObjects.Shader#glTexture
         * @type {?Phaser.Renderer.WebGL.Wrappers.WebGLTextureWrapper}
         * @since 3.19.0
         */
        this.glTexture = null;

        /**
         * A flag that indicates if this Shader has been set to render to a texture instead of the display list.
         *
         * This property is `true` if you have called `Shader.setRenderToTexture`, otherwise it's `false`.
         *
         * A Shader that is rendering to a texture _does not_ appear on the display list.
         *
         * @name Phaser.GameObjects.Shader#renderToTexture
         * @type {boolean}
         * @readonly
         * @since 3.19.0
         */
        this.renderToTexture = false;

        /**
         * A reference to the Phaser.Textures.Texture that has been stored in the Texture Manager for this Shader.
         *
         * This property is only set if you have called `Shader.setRenderToTexture` with a key, otherwise it is `null`.
         *
         * @name Phaser.GameObjects.Shader#texture
         * @type {Phaser.Textures.Texture}
         * @since 3.19.0
         */
        this.texture = null;

        this.setPosition(x, y);
        this.setSize(width, height);
        this.setOrigin(0.5, 0.5);
        this.setShader(key, textures, textureData);

        this.renderer.on(RenderEvents.RESTORE_WEBGL, this.onContextRestored, this);
    },

    /**
     * Compares the renderMask with the renderFlags to see if this Game Object will render or not.
     * Also checks the Game Object against the given Cameras exclusion list.
     *
     * @method Phaser.GameObjects.Shader#willRender
     * @since 3.0.0
     *
     * @param {Phaser.Cameras.Scene2D.Camera} camera - The Camera to check against this Game Object.
     *
     * @return {boolean} True if the Game Object should be rendered, otherwise false.
     */
    willRender: function (camera)
    {
        if (this.renderToTexture)
        {
            return true;
        }
        else
        {
            return !(GameObject.RENDER_MASK !== this.renderFlags || (this.cameraFilter !== 0 && (this.cameraFilter & camera.id)));
        }
    },

    /**
     * Changes this Shader so instead of rendering to the display list it renders to a
     * WebGL Framebuffer and WebGL Texture instead. This allows you to use the output
     * of this shader as an input for another shader, by mapping a sampler2D uniform
     * to it.
     *
     * After calling this method the `Shader.framebuffer` and `Shader.glTexture` properties
     * are populated.
     *
     * Additionally, you can provide a key to this method. Doing so will create a Phaser Texture
     * from this Shader and save it into the Texture Manager, allowing you to then use it for
     * any texture-based Game Object, such as a Sprite or Image:
     *
     * ```javascript
     * var shader = this.add.shader('myShader', x, y, width, height);
     *
     * shader.setRenderToTexture('doodle');
     *
     * this.add.image(400, 300, 'doodle');
     * ```
     *
     * Note that it stores an active reference to this Shader. That means as this shader updates,
     * so does the texture and any object using it to render with. Also, if you destroy this
     * shader, be sure to clear any objects that may have been using it as a texture too.
     *
     * You can access the Phaser Texture that is created via the `Shader.texture` property.
     *
     * By default it will create a single base texture. You can add frames to the texture
     * by using the `Texture.add` method. After doing this, you can then allow Game Objects
     * to use a specific frame from a Render Texture.
     *
     * @method Phaser.GameObjects.Shader#setRenderToTexture
     * @since 3.19.0
     *
     * @param {string} [key] - The unique key to store the texture as within the global Texture Manager.
     * @param {boolean} [flipY=false] - Does this texture need vertically flipping before rendering? This should usually be set to `true` if being fed from a buffer.
     *
     * @return {this} This Shader instance.
     */
    setRenderToTexture: function (key, flipY)
    {
        if (flipY === undefined) { flipY = false; }

        if (!this.renderToTexture)
        {
            var width = this.width;
            var height = this.height;
            var renderer = this.renderer;

            this.glTexture = renderer.createTextureFromSource(null, width, height, 0);

            this.glTexture.flipY = flipY;

            this.framebuffer = renderer.createFramebuffer(width, height, this.glTexture, false);

            this._rendererWidth = width;
            this._rendererHeight = height;

            this.renderToTexture = true;

            this.projOrtho(0, this.width, this.height, 0);

            if (key)
            {
                this.texture = this.scene.sys.textures.addGLTexture(key, this.glTexture);
            }
        }

        //  And now render at least once, so our texture isn't blank on the first update

        if (this.shader)
        {
            renderer.pipelines.clear();

            this.load();
            this.flush();

            renderer.pipelines.rebind();
        }

        return this;
    },

    /**
     * Sets the fragment and, optionally, the vertex shader source code that this Shader will use.
     * This will immediately delete the active shader program, if set, and then create a new one
     * with the given source. Finally, the shader uniforms are initialized.
     *
     * @method Phaser.GameObjects.Shader#setShader
     * @since 3.17.0
     *
     * @param {(string|Phaser.Display.BaseShader)} key - The key of the shader to use from the shader cache, or a BaseShader instance.
     * @param {string[]} [textures] - Optional array of texture keys to bind to the iChannel0...3 uniforms. The textures must already exist in the Texture Manager.
     * @param {any} [textureData] - Additional texture data.
     *
     * @return {this} This Shader instance.
     */
    setShader: function (key, textures, textureData)
    {
        if (this.renderer.contextLost)
        {
            this._deferSetShader = { key: key, textures: textures, textureData: textureData };
            return this;
        }

        if (textures === undefined) { textures = []; }

        if (typeof key === 'string')
        {
            var cache = this.scene.sys.cache.shader;

            if (!cache.has(key))
            {
                console.warn('Shader missing: ' + key);
                return this;
            }

            this.shader = cache.get(key);
        }
        else
        {
            this.shader = key;
        }

        var gl = this.gl;
        var renderer = this.renderer;

        if (this.program)
        {
            renderer.deleteProgram(this.program);
        }

        var program = renderer.createProgram(this.shader.vertexSrc, this.shader.fragmentSrc);

        //  The default uniforms available within the vertex shader
        gl.uniformMatrix4fv(gl.getUniformLocation(program.webGLProgram, 'uViewMatrix'), false, this.viewMatrix);
        gl.uniformMatrix4fv(gl.getUniformLocation(program.webGLProgram, 'uProjectionMatrix'), false, this.projectionMatrix);
        gl.uniform2f(gl.getUniformLocation(program.webGLProgram, 'uResolution'), this.width, this.height);

        this.program = program;

        var d = new Date();

        //  The default uniforms available within the fragment shader
        var defaultUniforms = {
            resolution: { type: '2f', value: { x: this.width, y: this.height } },
            time: { type: '1f', value: 0 },
            mouse: { type: '2f', value: { x: this.width / 2, y: this.height / 2 } },
            date: { type: '4fv', value: [ d.getFullYear(), d.getMonth(), d.getDate(), d.getHours() * 60 * 60 + d.getMinutes() * 60 + d.getSeconds() ] },
            sampleRate: { type: '1f', value: 44100.0 },
            iChannel0: { type: 'sampler2D', value: null, textureData: { repeat: true } },
            iChannel1: { type: 'sampler2D', value: null, textureData: { repeat: true } },
            iChannel2: { type: 'sampler2D', value: null, textureData: { repeat: true } },
            iChannel3: { type: 'sampler2D', value: null, textureData: { repeat: true } }
        };

        if (this.shader.uniforms)
        {
            this.uniforms = Extend(true, {}, this.shader.uniforms, defaultUniforms);
        }
        else
        {
            this.uniforms = defaultUniforms;
        }

        for (var i = 0; i < 4; i++)
        {
            if (textures[i])
            {
                this.setSampler2D('iChannel' + i, textures[i], i, textureData);
            }
        }

        this.initUniforms();

        this.projOrtho(0, this._rendererWidth, this._rendererHeight, 0);

        return this;
    },

    /**
     * Binds a Phaser Pointer object to this Shader.
     *
     * The screen position of the pointer will be set in to the shaders `mouse` uniform
     * automatically every frame. Call this method with no arguments to unbind the pointer.
     *
     * @method Phaser.GameObjects.Shader#setPointer
     * @since 3.17.0
     *
     * @param {Phaser.Input.Pointer} [pointer] - The Pointer to bind to this shader.
     *
     * @return {this} This Shader instance.
     */
    setPointer: function (pointer)
    {
        this.pointer = pointer;

        return this;
    },

    /**
     * Sets this shader to use an orthographic projection matrix.
     * This matrix is stored locally in the `projectionMatrix` property,
     * as well as being bound to the `uProjectionMatrix` uniform.
     *
     * @method Phaser.GameObjects.Shader#projOrtho
     * @since 3.17.0
     *
     * @param {number} left - The left value.
     * @param {number} right - The right value.
     * @param {number} bottom - The bottom value.
     * @param {number} top - The top value.
     */
    projOrtho: function (left, right, bottom, top)
    {
        if (this.renderer.contextLost)
        {
            this._deferProjOrtho = { left: left, right: right, bottom: bottom, top: top };
            return;
        }

        var near = -1000;
        var far = 1000;

        var leftRight = 1 / (left - right);
        var bottomTop = 1 / (bottom - top);
        var nearFar = 1 / (near - far);

        var pm = this.projectionMatrix;

        pm[0] = -2 * leftRight;
        pm[5] = -2 * bottomTop;
        pm[10] = 2 * nearFar;
        pm[12] = (left + right) * leftRight;
        pm[13] = (top + bottom) * bottomTop;
        pm[14] = (far + near) * nearFar;

        var program = this.program;

        var gl = this.gl;
        var renderer = this.renderer;

        renderer.setProgram(program);

        gl.uniformMatrix4fv(gl.getUniformLocation(program.webGLProgram, 'uProjectionMatrix'), false, this.projectionMatrix);

        this._rendererWidth = right;
        this._rendererHeight = bottom;
    },

    // Uniforms are specified in the GLSL_ES Specification: http://www.khronos.org/registry/webgl/specs/latest/1.0/
    // http://www.khronos.org/registry/gles/specs/2.0/GLSL_ES_Specification_1.0.17.pdf

    /**
     * Initializes all of the uniforms this shader uses.
     *
     * @method Phaser.GameObjects.Shader#initUniforms
     * @private
     * @since 3.17.0
     */
    initUniforms: function ()
    {
        var map = this.renderer.glFuncMap;
        var program = this.program;

        this._textureCount = 0;

        for (var key in this.uniforms)
        {
            var uniform = this.uniforms[key];

            var type = uniform.type;
            var data = map[type];

            uniform.uniformLocation = this.renderer.createUniformLocation(program, key);

            if (type !== 'sampler2D')
            {
                uniform.glMatrix = data.matrix;
                uniform.glValueLength = data.length;
                uniform.glFunc = data.func;
            }
        }
    },

    /**
     * Sets a sampler2D uniform on this shader where the source texture is a WebGLTextureBuffer.
     *
     * This allows you to feed the output from one Shader into another:
     *
     * ```javascript
     * let shader1 = this.add.shader(baseShader1, 0, 0, 512, 512).setRenderToTexture();
     * let shader2 = this.add.shader(baseShader2, 0, 0, 512, 512).setRenderToTexture('output');
     *
     * shader1.setSampler2DBuffer('iChannel0', shader2.glTexture, 512, 512);
     * shader2.setSampler2DBuffer('iChannel0', shader1.glTexture, 512, 512);
     * ```
     *
     * In the above code, the result of baseShader1 is fed into Shader2 as the `iChannel0` sampler2D uniform.
     * The result of baseShader2 is then fed back into shader1 again, creating a feedback loop.
     *
     * If you wish to use an image from the Texture Manager as a sampler2D input for this shader,
     * see the `Shader.setSampler2D` method.
     *
     * @method Phaser.GameObjects.Shader#setSampler2DBuffer
     * @since 3.19.0
     *
     * @param {string} uniformKey - The key of the sampler2D uniform to be updated, i.e. `iChannel0`.
     * @param {Phaser.Renderer.WebGL.Wrappers.WebGLTextureWrapper} texture - A texture reference.
     * @param {number} width - The width of the texture.
     * @param {number} height - The height of the texture.
     * @param {number} [textureIndex=0] - The texture index.
     * @param {any} [textureData] - Additional texture data.
     *
     * @return {this} This Shader instance.
     */
    setSampler2DBuffer: function (uniformKey, texture, width, height, textureIndex, textureData)
    {
        if (textureIndex === undefined) { textureIndex = 0; }
        if (textureData === undefined) { textureData = {}; }

        var uniform = this.uniforms[uniformKey];

        uniform.value = texture;

        textureData.width = width;
        textureData.height = height;

        uniform.textureData = textureData;

        this._textureCount = textureIndex;

        this.initSampler2D(uniform);

        return this;
    },

    /**
     * Sets a sampler2D uniform on this shader.
     *
     * The textureKey given is the key from the Texture Manager cache. You cannot use a single frame
     * from a texture, only the full image. Also, lots of shaders expect textures to be power-of-two sized.
     *
     * If you wish to use another Shader as a sampler2D input for this shader, see the `Shader.setSampler2DBuffer` method.
     *
     * @method Phaser.GameObjects.Shader#setSampler2D
     * @since 3.17.0
     *
     * @param {string} uniformKey - The key of the sampler2D uniform to be updated, i.e. `iChannel0`.
     * @param {string} textureKey - The key of the texture, as stored in the Texture Manager. Must already be loaded.
     * @param {number} [textureIndex=0] - The texture index.
     * @param {any} [textureData] - Additional texture data.
     *
     * @return {this} This Shader instance.
     */
    setSampler2D: function (uniformKey, textureKey, textureIndex, textureData)
    {
        if (textureIndex === undefined) { textureIndex = 0; }

        var textureManager = this.scene.sys.textures;

        if (textureManager.exists(textureKey))
        {
            var frame = textureManager.getFrame(textureKey);

            if (frame.glTexture && frame.glTexture.isRenderTexture)
            {
                return this.setSampler2DBuffer(uniformKey, frame.glTexture, frame.width, frame.height, textureIndex, textureData);
            }

            var uniform = this.uniforms[uniformKey];
            var source = frame.source;

            uniform.textureKey = textureKey;
            uniform.source = source.image;
            uniform.value = frame.glTexture;

            if (source.isGLTexture)
            {
                if (!textureData)
                {
                    textureData = {};
                }

                textureData.width = source.width;
                textureData.height = source.height;
            }

            if (textureData)
            {
                uniform.textureData = textureData;
            }

            this._textureCount = textureIndex;

            this.initSampler2D(uniform);
        }

        return this;
    },

    /**
     * Sets a property of a uniform already present on this shader.
     *
     * To modify the value of a uniform such as a 1f or 1i use the `value` property directly:
     *
     * ```javascript
     * shader.setUniform('size.value', 16);
     * ```
     *
     * You can use dot notation to access deeper values, for example:
     *
     * ```javascript
     * shader.setUniform('resolution.value.x', 512);
     * ```
     *
     * The change to the uniform will take effect the next time the shader is rendered.
     *
     * @method Phaser.GameObjects.Shader#setUniform
     * @since 3.17.0
     *
     * @param {string} key - The key of the uniform to modify. Use dots for deep properties, i.e. `resolution.value.x`.
     * @param {any} value - The value to set into the uniform.
     *
     * @return {this} This Shader instance.
     */
    setUniform: function (key, value)
    {
        SetValue(this.uniforms, key, value);

        return this;
    },

    /**
     * Returns the uniform object for the given key, or `null` if the uniform couldn't be found.
     *
     * @method Phaser.GameObjects.Shader#getUniform
     * @since 3.17.0
     *
     * @param {string} key - The key of the uniform to return the value for.
     *
     * @return {any} A reference to the uniform object. This is not a copy, so modifying it will update the original object also.
     */
    getUniform: function (key)
    {
        return GetFastValue(this.uniforms, key, null);
    },

    /**
     * A short-cut method that will directly set the texture being used by the `iChannel0` sampler2D uniform.
     *
     * The textureKey given is the key from the Texture Manager cache. You cannot use a single frame
     * from a texture, only the full image. Also, lots of shaders expect textures to be power-of-two sized.
     *
     * @method Phaser.GameObjects.Shader#setChannel0
     * @since 3.17.0
     *
     * @param {string} textureKey - The key of the texture, as stored in the Texture Manager. Must already be loaded.
     * @param {any} [textureData] - Additional texture data.
     *
     * @return {this} This Shader instance.
     */
    setChannel0: function (textureKey, textureData)
    {
        return this.setSampler2D('iChannel0', textureKey, 0, textureData);
    },

    /**
     * A short-cut method that will directly set the texture being used by the `iChannel1` sampler2D uniform.
     *
     * The textureKey given is the key from the Texture Manager cache. You cannot use a single frame
     * from a texture, only the full image. Also, lots of shaders expect textures to be power-of-two sized.
     *
     * @method Phaser.GameObjects.Shader#setChannel1
     * @since 3.17.0
     *
     * @param {string} textureKey - The key of the texture, as stored in the Texture Manager. Must already be loaded.
     * @param {any} [textureData] - Additional texture data.
     *
     * @return {this} This Shader instance.
     */
    setChannel1: function (textureKey, textureData)
    {
        return this.setSampler2D('iChannel1', textureKey, 1, textureData);
    },

    /**
     * A short-cut method that will directly set the texture being used by the `iChannel2` sampler2D uniform.
     *
     * The textureKey given is the key from the Texture Manager cache. You cannot use a single frame
     * from a texture, only the full image. Also, lots of shaders expect textures to be power-of-two sized.
     *
     * @method Phaser.GameObjects.Shader#setChannel2
     * @since 3.17.0
     *
     * @param {string} textureKey - The key of the texture, as stored in the Texture Manager. Must already be loaded.
     * @param {any} [textureData] - Additional texture data.
     *
     * @return {this} This Shader instance.
     */
    setChannel2: function (textureKey, textureData)
    {
        return this.setSampler2D('iChannel2', textureKey, 2, textureData);
    },

    /**
     * A short-cut method that will directly set the texture being used by the `iChannel3` sampler2D uniform.
     *
     * The textureKey given is the key from the Texture Manager cache. You cannot use a single frame
     * from a texture, only the full image. Also, lots of shaders expect textures to be power-of-two sized.
     *
     * @method Phaser.GameObjects.Shader#setChannel3
     * @since 3.17.0
     *
     * @param {string} textureKey - The key of the texture, as stored in the Texture Manager. Must already be loaded.
     * @param {any} [textureData] - Additional texture data.
     *
     * @return {this} This Shader instance.
     */
    setChannel3: function (textureKey, textureData)
    {
        return this.setSampler2D('iChannel3', textureKey, 3, textureData);
    },

    /**
     * Internal method that takes a sampler2D uniform and prepares it for use by setting the
     * gl texture parameters.
     *
     * @method Phaser.GameObjects.Shader#initSampler2D
     * @private
     * @since 3.17.0
     *
     * @param {any} uniform - The sampler2D uniform to process.
     */
    initSampler2D: function (uniform)
    {
        if (!uniform.value)
        {
            return;
        }

        //  Extended texture data

        var data = uniform.textureData;

        if (data && !uniform.value.isRenderTexture)
        {
            var gl = this.gl;
            var wrapper = uniform.value;

            // https://developer.mozilla.org/en-US/docs/Web/API/WebGLRenderingContext/texImage2D

            //  mag / minFilter can be: gl.LINEAR, gl.LINEAR_MIPMAP_LINEAR or gl.NEAREST
            //  wrapS/T can be: gl.CLAMP_TO_EDGE or gl.REPEAT
            //  format can be: gl.LUMINANCE or gl.RGBA

            var magFilter = gl[GetFastValue(data, 'magFilter', 'linear').toUpperCase()];
            var minFilter = gl[GetFastValue(data, 'minFilter', 'linear').toUpperCase()];
            var wrapS = gl[GetFastValue(data, 'wrapS', 'repeat').toUpperCase()];
            var wrapT = gl[GetFastValue(data, 'wrapT', 'repeat').toUpperCase()];
            var format = gl[GetFastValue(data, 'format', 'rgba').toUpperCase()];
            var flipY = GetFastValue(data, 'flipY', false);
            var width = GetFastValue(data, 'width', wrapper.width);
            var height = GetFastValue(data, 'height', wrapper.height);
            var source = GetFastValue(data, 'source', wrapper.pixels);

            if (data.repeat)
            {
                wrapS = gl.REPEAT;
                wrapT = gl.REPEAT;
            }

            if (data.width)
            {
                // If the uniform has resolution, use a blank texture.
                source = null;
            }

            wrapper.update(source, width, height, flipY, wrapS, wrapT, minFilter, magFilter, format);
        }

        this.renderer.setProgram(this.program);

        this._textureCount++;
    },

    /**
     * Synchronizes all of the uniforms this shader uses.
     * Each uniforms gl function is called in turn.
     *
     * @method Phaser.GameObjects.Shader#syncUniforms
     * @private
     * @since 3.17.0
     */
    syncUniforms: function ()
    {
        var gl = this.gl;

        var uniforms = this.uniforms;
        var uniform;
        var length;
        var glFunc;
        var location;
        var value;
        var textureCount = 0;

        for (var key in uniforms)
        {
            uniform = uniforms[key];

            glFunc = uniform.glFunc;
            length = uniform.glValueLength;
            location = uniform.uniformLocation;
            value = uniform.value;

            if (value === null)
            {
                continue;
            }

            if (length === 1)
            {
                if (uniform.glMatrix)
                {
                    glFunc.call(gl, location.webGLUniformLocation, uniform.transpose, value);
                }
                else
                {
                    glFunc.call(gl, location.webGLUniformLocation, value);
                }
            }
            else if (length === 2)
            {
                glFunc.call(gl, location.webGLUniformLocation, value.x, value.y);
            }
            else if (length === 3)
            {
                glFunc.call(gl, location.webGLUniformLocation, value.x, value.y, value.z);
            }
            else if (length === 4)
            {
                glFunc.call(gl, location.webGLUniformLocation, value.x, value.y, value.z, value.w);
            }
            else if (uniform.type === 'sampler2D')
            {
                gl.activeTexture(gl.TEXTURE0 + textureCount);

                gl.bindTexture(gl.TEXTURE_2D, value.webGLTexture);

                gl.uniform1i(location.webGLUniformLocation, textureCount);

                textureCount++;
            }
        }
    },

    /**
     * Called automatically during render.
     *
     * This method performs matrix ITRS and then stores the resulting value in the `uViewMatrix` uniform.
     * It then sets up the vertex buffer and shader, updates and syncs the uniforms ready
     * for flush to be called.
     *
     * @method Phaser.GameObjects.Shader#load
     * @since 3.17.0
     *
     * @param {Phaser.GameObjects.Components.TransformMatrix} [matrix2D] - The transform matrix to use during rendering.
     */
    load: function (matrix2D)
    {
        //  ITRS

        var gl = this.gl;
        var width = this.width;
        var height = this.height;
        var renderer = this.renderer;
        var program = this.program;
        var vm = this.viewMatrix;

        if (!this.renderToTexture)
        {
            var x = -this._displayOriginX;
            var y = -this._displayOriginY;

            vm[0] = matrix2D[0];
            vm[1] = matrix2D[1];
            vm[4] = matrix2D[2];
            vm[5] = matrix2D[3];
            vm[8] = matrix2D[4];
            vm[9] = matrix2D[5];
            vm[12] = vm[0] * x + vm[4] * y;
            vm[13] = vm[1] * x + vm[5] * y;
        }

        //  Update vertex shader uniforms

        gl.useProgram(program.webGLProgram);

        gl.uniformMatrix4fv(gl.getUniformLocation(program.webGLProgram, 'uViewMatrix'), false, vm);
        gl.uniformMatrix4fv(gl.getUniformLocation(program.webGLProgram, 'uProjectionMatrix'), false, this.projectionMatrix);
        gl.uniform2f(gl.getUniformLocation(program.webGLProgram, 'uResolution'), this.width, this.height);

        //  Update fragment shader uniforms

        var uniforms = this.uniforms;
        var res = uniforms.resolution;

        res.value.x = width;
        res.value.y = height;

        uniforms.time.value = renderer.game.loop.getDuration();

        var pointer = this.pointer;

        if (pointer)
        {
            var mouse = uniforms.mouse;

            var px = pointer.x / width;
            var py = 1 - pointer.y / height;

            mouse.value.x = px.toFixed(2);
            mouse.value.y = py.toFixed(2);
        }

        this.syncUniforms();
    },

    /**
     * Called automatically during render.
     *
     * Sets the active shader, loads the vertex buffer and then draws.
     *
     * @method Phaser.GameObjects.Shader#flush
     * @since 3.17.0
     */
    flush: function ()
    {
        //  Bind

        var width = this.width;
        var height = this.height;
        var program = this.program;

        var gl = this.gl;
        var vertexBuffer = this.vertexBuffer;
        var renderer = this.renderer;
        var vertexSize = Float32Array.BYTES_PER_ELEMENT * 2;

        if (this.renderToTexture)
        {
            renderer.setFramebuffer(this.framebuffer);

            gl.clearColor(0, 0, 0, 0);

            gl.clear(gl.COLOR_BUFFER_BIT);
        }

        gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer.webGLBuffer);

        var location = gl.getAttribLocation(program.webGLProgram, 'inPosition');

        if (location !== -1)
        {
            gl.enableVertexAttribArray(location);

            gl.vertexAttribPointer(location, 2, gl.FLOAT, false, vertexSize, 0);
        }

        //  Draw

        var vf = this.vertexViewF32;

        vf[3] = height;
        vf[4] = width;
        vf[5] = height;
        vf[8] = width;
        vf[9] = height;
        vf[10] = width;

        //  Flush

        var vertexCount = 6;

        gl.bufferSubData(gl.ARRAY_BUFFER, 0, this.bytes.subarray(0, vertexCount * vertexSize));

        gl.drawArrays(gl.TRIANGLES, 0, vertexCount);

        if (this.renderToTexture)
        {
            renderer.setFramebuffer(null, false);
        }
    },

    /**
     * A NOOP method so you can pass a Shader to a Container.
     * Calling this method will do nothing. It is intentionally empty.
     *
     * @method Phaser.GameObjects.Shader#setAlpha
     * @private
     * @since 3.17.0
     */
    setAlpha: function ()
    {
    },

    /**
     * A NOOP method so you can pass a Shader to a Container.
     * Calling this method will do nothing. It is intentionally empty.
     *
     * @method Phaser.GameObjects.Shader#setBlendMode
     * @private
     * @since 3.17.0
     */
    setBlendMode: function ()
    {
    },

    /**
     * Run any logic that was deferred during context loss.
     *
     * @method Phaser.GameObjects.Shader#onContextRestored
     * @since 3.80.0
     */
    onContextRestored: function ()
    {
        if (this._deferSetShader !== null)
        {
            var key = this._deferSetShader.key;
            var textures = this._deferSetShader.textures;
            var textureData = this._deferSetShader.textureData;
            this._deferSetShader = null;
            this.setShader(key, textures, textureData);
        }

        if (this._deferProjOrtho !== null)
        {
            var left = this._deferProjOrtho.left;
            var right = this._deferProjOrtho.right;
            var bottom = this._deferProjOrtho.bottom;
            var top = this._deferProjOrtho.top;
            this._deferProjOrtho = null;
            this.projOrtho(left, right, bottom, top);
        }
    },

    /**
     * Internal destroy handler, called as part of the destroy process.
     *
     * @method Phaser.GameObjects.Shader#preDestroy
     * @protected
     * @since 3.17.0
     */
    preDestroy: function ()
    {
        var renderer = this.renderer;

        renderer.off(RenderEvents.RESTORE_WEBGL, this.onContextRestored, this);
        renderer.deleteProgram(this.program);
        renderer.deleteBuffer(this.vertexBuffer);

        if (this.renderToTexture)
        {
            renderer.deleteFramebuffer(this.framebuffer);

            this.texture.destroy();

            this.framebuffer = null;
            this.glTexture = null;
            this.texture = null;
        }

        ArrayEach(this.uniforms, function (uniform)
        {
            renderer.deleteUniformLocation(uniform.uniformLocation);
            uniform.uniformLocation = null;
        });
    }

});

module.exports = Shader;


/**
* The `Matter.Bounds` module contains methods for creating and manipulating axis-aligned bounding boxes (AABB).
*
* @class Bounds
*/

var Bounds = {};

module.exports = Bounds;

(function() {

    /**
     * Creates a new axis-aligned bounding box (AABB) for the given vertices.
     * @method create
     * @param {vertices} vertices
     * @return {bounds} A new bounds object
     */
    Bounds.create = function(vertices) {
        var bounds = {
            min: { x: 0, y: 0 },
            max: { x: 0, y: 0 }
        };

        if (vertices)
            Bounds.update(bounds, vertices);

        return bounds;
    };

    /**
     * Updates bounds using the given vertices and extends the bounds given a velocity.
     * @method update
     * @param {bounds} bounds
     * @param {vertices} vertices
     * @param {vector} velocity
     */
    Bounds.update = function(bounds, vertices, velocity) {
        bounds.min.x = Infinity;
        bounds.max.x = -Infinity;
        bounds.min.y = Infinity;
        bounds.max.y = -Infinity;

        for (var i = 0; i < vertices.length; i++) {
            var vertex = vertices[i];
            if (vertex.x > bounds.max.x) bounds.max.x = vertex.x;
            if (vertex.x < bounds.min.x) bounds.min.x = vertex.x;
            if (vertex.y > bounds.max.y) bounds.max.y = vertex.y;
            if (vertex.y < bounds.min.y) bounds.min.y = vertex.y;
        }

        if (velocity) {
            if (velocity.x > 0) {
                bounds.max.x += velocity.x;
            } else {
                bounds.min.x += velocity.x;
            }

            if (velocity.y > 0) {
                bounds.max.y += velocity.y;
            } else {
                bounds.min.y += velocity.y;
            }
        }
    };

    /**
     * Returns true if the bounds contains the given point.
     * @method contains
     * @param {bounds} bounds
     * @param {vector} point
     * @return {boolean} True if the bounds contain the point, otherwise false
     */
    Bounds.contains = function(bounds, point) {
        return point.x >= bounds.min.x && point.x <= bounds.max.x
               && point.y >= bounds.min.y && point.y <= bounds.max.y;
    };

    /**
     * Returns true if the two bounds intersect.
     * @method overlaps
     * @param {bounds} boundsA
     * @param {bounds} boundsB
     * @return {boolean} True if the bounds overlap, otherwise false
     */
    Bounds.overlaps = function(boundsA, boundsB) {
        return (boundsA.min.x <= boundsB.max.x && boundsA.max.x >= boundsB.min.x
                && boundsA.max.y >= boundsB.min.y && boundsA.min.y <= boundsB.max.y);
    };

    /**
     * Translates the bounds by the given vector.
     * @method translate
     * @param {bounds} bounds
     * @param {vector} vector
     */
    Bounds.translate = function(bounds, vector) {
        bounds.min.x += vector.x;
        bounds.max.x += vector.x;
        bounds.min.y += vector.y;
        bounds.max.y += vector.y;
    };

    /**
     * Shifts the bounds to the given position.
     * @method shift
     * @param {bounds} bounds
     * @param {vector} position
     */
    Bounds.shift = function(bounds, position) {
        var deltaX = bounds.max.x - bounds.min.x,
            deltaY = bounds.max.y - bounds.min.y;

        bounds.min.x = position.x;
        bounds.max.x = position.x + deltaX;
        bounds.min.y = position.y;
        bounds.max.y = position.y + deltaY;
    };

})();


module.exports = [
    '#define SHADER_NAME PHASER_POINTLIGHT_FS',
    'precision mediump float;',
    'uniform vec2 uResolution;',
    'uniform float uCameraZoom;',
    'varying vec4 lightPosition;',
    'varying vec4 lightColor;',
    'varying float lightRadius;',
    'varying float lightAttenuation;',
    'void main ()',
    '{',
    '    vec2 center = (lightPosition.xy + 1.0) * (uResolution.xy * 0.5);',
    '    float distToSurf = length(center - gl_FragCoord.xy);',
    '    float radius = 1.0 - distToSurf / (lightRadius * uCameraZoom);',
    '    float intensity = smoothstep(0.0, 1.0, radius * lightAttenuation);',
    '    vec4 color = vec4(intensity, intensity, intensity, 0.0) * lightColor;',
    '    gl_FragColor = vec4(color.rgb * lightColor.a, color.a);',
    '}',
].join('\n');


/**
 * @author       Richard Davey <rich@phaser.io>
 * @copyright    2013-2024 Phaser Studio Inc.
 * @license      {@link https://opensource.org/licenses/MIT|MIT License}
 */

var LineToLine = require('./LineToLine');

/**
 * Checks if a Triangle and a Line intersect.
 *
 * The Line intersects the Triangle if it starts inside of it, ends inside of it, or crosses any of the Triangle's sides. Thus, the Triangle is considered "solid".
 *
 * @function Phaser.Geom.Intersects.TriangleToLine
 * @since 3.0.0
 *
 * @param {Phaser.Geom.Triangle} triangle - The Triangle to check with.
 * @param {Phaser.Geom.Line} line - The Line to check with.
 *
 * @return {boolean} `true` if the Triangle and the Line intersect, otherwise `false`.
 */
var TriangleToLine = function (triangle, line)
{
    //  If the Triangle contains either the start or end point of the line, it intersects
    if (triangle.contains(line.x1, line.y1) || triangle.contains(line.x2, line.y2))
    {
        return true;
    }

    //  Now check the line against each line of the Triangle
    if (LineToLine(triangle.getLineA(), line))
    {
        return true;
    }

    if (LineToLine(triangle.getLineB(), line))
    {
        return true;
    }

    if (LineToLine(triangle.getLineC(), line))
    {
        return true;
    }

    return false;
};

module.exports = TriangleToLine;


//  Auto-invert the flipY if this is coming from a GL RenderTexture

if (gameObject.flipY || (frame.source.isGLTexture && frame.source.isRenderTexture && !texture.flipY))

keep keep keep replace replace replace keep keep keep

# Version 3.80.1 - Nino - 27 February 2024

# Bug Fixes

* Fix `RenderTexture` crashing in the presence of a light.
* Fix failure to restore compressed textures after WebGL context loss.
* Fix a single WebGL error, with no visual side-effects, from occurring while calling `Shader.setRenderToTexture()` after the game has started running. Actually, the root cause was leaving new WebGL textures bound after creation.
* Ensure that `TextureSource.setFlipY` always updates the texture.


/**
 * @author       Richard Davey <rich@phaser.io>
 * @copyright    2013-2024 Phaser Studio Inc.
 * @license      {@link https://opensource.org/licenses/MIT|MIT License}
 */

var Class = require('../../utils/Class');
var FileTypesManager = require('../FileTypesManager');
var GetFastValue = require('../../utils/object/GetFastValue');
var ImageFile = require('./ImageFile.js');
var IsPlainObject = require('../../utils/object/IsPlainObject');
var JSONFile = require('./JSONFile.js');
var MultiFile = require('../MultiFile.js');

/**
 * @classdesc
 * A single JSON based Texture Atlas File suitable for loading by the Loader.
 *
 * These are created when you use the Phaser.Loader.LoaderPlugin#atlas method and are not typically created directly.
 *
 * For documentation about what all the arguments and configuration options mean please see Phaser.Loader.LoaderPlugin#atlas.
 *
 * https://www.codeandweb.com/texturepacker/tutorials/how-to-create-sprite-sheets-for-phaser3?source=photonstorm
 *
 * @class AsepriteFile
 * @extends Phaser.Loader.MultiFile
 * @memberof Phaser.Loader.FileTypes
 * @constructor
 * @since 3.50.0
 *
 * @param {Phaser.Loader.LoaderPlugin} loader - A reference to the Loader that is responsible for this file.
 * @param {(string|Phaser.Types.Loader.FileTypes.AsepriteFileConfig)} key - The key to use for this file, or a file configuration object.
 * @param {string|string[]} [textureURL] - The absolute or relative URL to load the texture image file from. If undefined or `null` it will be set to `<key>.png`, i.e. if `key` was "alien" then the URL will be "alien.png".
 * @param {object|string} [atlasURL] - The absolute or relative URL to load the texture atlas json data file from. If undefined or `null` it will be set to `<key>.json`, i.e. if `key` was "alien" then the URL will be "alien.json". Or, a well formed JSON object.
 * @param {Phaser.Types.Loader.XHRSettingsObject} [textureXhrSettings] - An XHR Settings configuration object for the atlas image file. Used in replacement of the Loaders default XHR Settings.
 * @param {Phaser.Types.Loader.XHRSettingsObject} [atlasXhrSettings] - An XHR Settings configuration object for the atlas json file. Used in replacement of the Loaders default XHR Settings.
 */
var AsepriteFile = new Class({

    Extends: MultiFile,

    initialize:

    function AsepriteFile (loader, key, textureURL, atlasURL, textureXhrSettings, atlasXhrSettings)
    {
        var image;
        var data;

        if (IsPlainObject(key))
        {
            var config = key;

            key = GetFastValue(config, 'key');

            image = new ImageFile(loader, {
                key: key,
                url: GetFastValue(config, 'textureURL'),
                extension: GetFastValue(config, 'textureExtension', 'png'),
                normalMap: GetFastValue(config, 'normalMap'),
                xhrSettings: GetFastValue(config, 'textureXhrSettings')
            });

            data = new JSONFile(loader, {
                key: key,
                url: GetFastValue(config, 'atlasURL'),
                extension: GetFastValue(config, 'atlasExtension', 'json'),
                xhrSettings: GetFastValue(config, 'atlasXhrSettings')
            });
        }
        else
        {
            image = new ImageFile(loader, key, textureURL, textureXhrSettings);
            data = new JSONFile(loader, key, atlasURL, atlasXhrSettings);
        }

        if (image.linkFile)
        {
            //  Image has a normal map
            MultiFile.call(this, loader, 'atlasjson', key, [ image, data, image.linkFile ]);
        }
        else
        {
            MultiFile.call(this, loader, 'atlasjson', key, [ image, data ]);
        }
    },

    /**
     * Adds this file to its target cache upon successful loading and processing.
     *
     * @method Phaser.Loader.FileTypes.AsepriteFile#addToCache
     * @since 3.7.0
     */
    addToCache: function ()
    {
        if (this.isReadyToProcess())
        {
            var image = this.files[0];
            var json = this.files[1];
            var normalMap = (this.files[2]) ? this.files[2].data : null;

            this.loader.textureManager.addAtlas(image.key, image.data, json.data, normalMap);

            json.addToCache();

            this.complete = true;
        }
    }

});

/**
 * Aseprite is a powerful animated sprite editor and pixel art tool.
 *
 * You can find more details at https://www.aseprite.org/
 *
 * Adds a JSON based Aseprite Animation, or array of animations, to the current load queue.
 *
 * You can call this method from within your Scene's `preload`, along with any other files you wish to load:
 *
 * ```javascript
 * function preload ()
 * {
 *     this.load.aseprite('gladiator', 'images/Gladiator.png', 'images/Gladiator.json');
 * }
 * ```
 *
 * The file is **not** loaded right away. It is added to a queue ready to be loaded either when the loader starts,
 * or if it's already running, when the next free load slot becomes available. This happens automatically if you
 * are calling this from within the Scene's `preload` method, or a related callback. Because the file is queued
 * it means you cannot use the file immediately after calling this method, but must wait for the file to complete.
 * The typical flow for a Phaser Scene is that you load assets in the Scene's `preload` method and then when the
 * Scene's `create` method is called you are guaranteed that all of those assets are ready for use and have been
 * loaded.
 *
 * If you call this from outside of `preload` then you are responsible for starting the Loader afterwards and monitoring
 * its events to know when it's safe to use the asset. Please see the Phaser.Loader.LoaderPlugin class for more details.
 *
 * To export a compatible JSON file in Aseprite, please do the following:
 *
 * 1. Go to "File - Export Sprite Sheet"
 *
 * 2. On the **Layout** tab:
 * 2a. Set the "Sheet type" to "Packed"
 * 2b. Set the "Constraints" to "None"
 * 2c. Check the "Merge Duplicates" checkbox
 *
 * 3. On the **Sprite** tab:
 * 3a. Set "Layers" to "Visible layers"
 * 3b. Set "Frames" to "All frames", unless you only wish to export a sub-set of tags
 *
 * 4. On the **Borders** tab:
 * 4a. Check the "Trim Sprite" and "Trim Cells" options
 * 4b. Ensure "Border Padding", "Spacing" and "Inner Padding" are all > 0 (1 is usually enough)
 *
 * 5. On the **Output** tab:
 * 5a. Check "Output File", give your image a name and make sure you choose "png files" as the file type
 * 5b. Check "JSON Data" and give your json file a name
 * 5c. The JSON Data type can be either a Hash or Array, Phaser doesn't mind.
 * 5d. Make sure "Tags" is checked in the Meta options
 * 5e. In the "Item Filename" input box, make sure it says just "{frame}" and nothing more.
 *
 * 6. Click export
 *
 * This was tested with Aseprite 1.2.25.
 *
 * This will export a png and json file which you can load using the Aseprite Loader, i.e.:
 *
 * Phaser can load all common image types: png, jpg, gif and any other format the browser can natively handle.
 *
 * The key must be a unique String. It is used to add the file to the global Texture Manager upon a successful load.
 * The key should be unique both in terms of files being loaded and files already present in the Texture Manager.
 * Loading a file using a key that is already taken will result in a warning. If you wish to replace an existing file
 * then remove it from the Texture Manager first, before loading a new one.
 *
 * Instead of passing arguments you can pass a configuration object, such as:
 *
 * ```javascript
 * this.load.aseprite({
 *     key: 'gladiator',
 *     textureURL: 'images/Gladiator.png',
 *     atlasURL: 'images/Gladiator.json'
 * });
 * ```
 *
 * See the documentation for `Phaser.Types.Loader.FileTypes.AsepriteFileConfig` for more details.
 *
 * Instead of passing a URL for the JSON data you can also pass in a well formed JSON object instead.
 *
 * Once loaded, you can call this method from within a Scene with the 'atlas' key:
 *
 * ```javascript
 * this.anims.createFromAseprite('paladin');
 * ```
 *
 * Any animations defined in the JSON will now be available to use in Phaser and you play them
 * via their Tag name. For example, if you have an animation called 'War Cry' on your Aseprite timeline,
 * you can play it in Phaser using that Tag name:
 *
 * ```javascript
 * this.add.sprite(400, 300).play('War Cry');
 * ```
 *
 * When calling this method you can optionally provide an array of tag names, and only those animations
 * will be created. For example:
 *
 * ```javascript
 * this.anims.createFromAseprite('paladin', [ 'step', 'War Cry', 'Magnum Break' ]);
 * ```
 *
 * This will only create the 3 animations defined. Note that the tag names are case-sensitive.
 *
 * If you have specified a prefix in the loader, via `Loader.setPrefix` then this value will be prepended to this files
 * key. For example, if the prefix was `MENU.` and the key was `Background` the final key will be `MENU.Background` and
 * this is what you would use to retrieve the image from the Texture Manager.
 *
 * The URL can be relative or absolute. If the URL is relative the `Loader.baseURL` and `Loader.path` values will be prepended to it.
 *
 * If the URL isn't specified the Loader will take the key and create a filename from that. For example if the key is "alien"
 * and no URL is given then the Loader will set the URL to be "alien.png". It will always add `.png` as the extension, although
 * this can be overridden if using an object instead of method arguments. If you do not desire this action then provide a URL.
 *
 * Note: The ability to load this type of file will only be available if the Aseprite File type has been built into Phaser.
 * It is available in the default build but can be excluded from custom builds.
 *
 * @method Phaser.Loader.LoaderPlugin#aseprite
 * @fires Phaser.Loader.Events#ADD
 * @since 3.50.0
 *
 * @param {(string|Phaser.Types.Loader.FileTypes.AsepriteFileConfig|Phaser.Types.Loader.FileTypes.AsepriteFileConfig[])} key - The key to use for this file, or a file configuration object, or array of them.
 * @param {string|string[]} [textureURL] - The absolute or relative URL to load the texture image file from. If undefined or `null` it will be set to `<key>.png`, i.e. if `key` was "alien" then the URL will be "alien.png".
 * @param {object|string} [atlasURL] - The absolute or relative URL to load the texture atlas json data file from. If undefined or `null` it will be set to `<key>.json`, i.e. if `key` was "alien" then the URL will be "alien.json". Or, a well formed JSON object.
 * @param {Phaser.Types.Loader.XHRSettingsObject} [textureXhrSettings] - An XHR Settings configuration object for the atlas image file. Used in replacement of the Loaders default XHR Settings.
 * @param {Phaser.Types.Loader.XHRSettingsObject} [atlasXhrSettings] - An XHR Settings configuration object for the atlas json file. Used in replacement of the Loaders default XHR Settings.
 *
 * @return {this} The Loader instance.
 */
FileTypesManager.register('aseprite', function (key, textureURL, atlasURL, textureXhrSettings, atlasXhrSettings)
{
    var multifile;

    //  Supports an Object file definition in the key argument
    //  Or an array of objects in the key argument
    //  Or a single entry where all arguments have been defined

    if (Array.isArray(key))
    {
        for (var i = 0; i < key.length; i++)
        {
            multifile = new AsepriteFile(this, key[i]);

            this.addFile(multifile.files);
        }
    }
    else
    {
        multifile = new AsepriteFile(this, key, textureURL, atlasURL, textureXhrSettings, atlasXhrSettings);

        this.addFile(multifile.files);
    }

    return this;
});

module.exports = AsepriteFile;


/**
 * @author       Richard Davey <rich@phaser.io>
 * @copyright    2013-2024 Phaser Studio Inc.
 * @license      {@link https://opensource.org/licenses/MIT|MIT License}
 */

var Class = require('../../utils/Class');
var GameObjectFactory = require('../../gameobjects/GameObjectFactory');

/**
 * @classdesc
 * A Bitmap Mask combines the alpha (opacity) of a masked pixel with the alpha of another pixel.
 * Unlike the Geometry Mask, which is a clipping path, a Bitmap Mask behaves like an alpha mask,
 * not a clipping path. It is only available when using the WebGL Renderer.
 *
 * A Bitmap Mask can use any Game Object or Dynamic Texture to determine the alpha of each pixel of the masked Game Object(s).
 * For any given point of a masked Game Object's texture, the pixel's alpha will be multiplied by the alpha
 * of the pixel at the same position in the Bitmap Mask's Game Object. The color of the pixel from the
 * Bitmap Mask doesn't matter.
 *
 * For example, if a pure blue pixel with an alpha of 0.95 is masked with a pure red pixel with an
 * alpha of 0.5, the resulting pixel will be pure blue with an alpha of 0.475. Naturally, this means
 * that a pixel in the mask with an alpha of 0 will hide the corresponding pixel in all masked Game Objects.
 * A pixel with an alpha of 1 in the masked Game Object will receive the same alpha as the
 * corresponding pixel in the mask.
 *
 * Note: You cannot combine Bitmap Masks and Blend Modes on the same Game Object. You can, however,
 * combine Geometry Masks and Blend Modes together.
 *
 * The Bitmap Mask's location matches the location of its Game Object, not the location of the
 * masked objects. Moving or transforming the underlying Game Object will change the mask
 * (and affect the visibility of any masked objects), whereas moving or transforming a masked object
 * will not affect the mask.
 *
 * The Bitmap Mask will not render its Game Object by itself. If the Game Object is not in a
 * Scene's display list, it will only be used for the mask and its full texture will not be directly
 * visible. Adding the underlying Game Object to a Scene will not cause any problems - it will
 * render as a normal Game Object and will also serve as a mask.
 *
 * @class BitmapMask
 * @memberof Phaser.Display.Masks
 * @constructor
 * @since 3.0.0
 *
 * @param {Phaser.Scene} scene - The Scene to which this mask is being added.
 * @param {(Phaser.GameObjects.GameObject|Phaser.Textures.DynamicTexture)} [maskObject] - The Game Object or Dynamic Texture that will be used as the mask. If `null` it will generate an Image Game Object using the rest of the arguments.
 * @param {number} [x] - If creating a Game Object, the horizontal position in the world.
 * @param {number} [y] - If creating a Game Object, the vertical position in the world.
 * @param {(string|Phaser.Textures.Texture)} [texture] - If creating a Game Object, the key, or instance of the Texture it will use to render with, as stored in the Texture Manager.
 * @param {(string|number|Phaser.Textures.Frame)} [frame] - If creating a Game Object, an optional frame from the Texture this Game Object is rendering with.
 */
var BitmapMask = new Class({

    initialize:

    function BitmapMask (scene, maskObject, x, y, texture, frame)
    {
        if (!maskObject)
        {
            maskObject = scene.sys.make.image({ x: x, y: y, key: texture, frame: frame, add: false });
        }

        /**
         * The Game Object that is used as the mask. Must use a texture, such as a Sprite.
         *
         * @name Phaser.Display.Masks.BitmapMask#bitmapMask
         * @type {(Phaser.GameObjects.GameObject|Phaser.Textures.DynamicTexture)}
         * @since 3.0.0
         */
        this.bitmapMask = maskObject;

        /**
         * Whether to invert the masks alpha.
         *
         * If `true`, the alpha of the masking pixel will be inverted before it's multiplied with the masked pixel.
         *
         * Essentially, this means that a masked area will be visible only if the corresponding area in the mask is invisible.
         *
         * @name Phaser.Display.Masks.BitmapMask#invertAlpha
         * @type {boolean}
         * @since 3.1.2
         */
        this.invertAlpha = false;

        /**
         * Is this mask a stencil mask? This is false by default and should not be changed.
         *
         * @name Phaser.Display.Masks.BitmapMask#isStencil
         * @type {boolean}
         * @readonly
         * @since 3.17.0
         */
        this.isStencil = false;
    },

    /**
     * Sets a new Game Object or Dynamic Texture for this Bitmap Mask to use.
     *
     * If a Game Object it must have a texture, such as a Sprite.
     *
     * You can update the source of the mask as often as you like.
     *
     * @method Phaser.Display.Masks.BitmapMask#setBitmap
     * @since 3.0.0
     *
     * @param {(Phaser.GameObjects.GameObject|Phaser.Textures.DynamicTexture)} maskObject - The Game Object or Dynamic Texture that will be used as the mask. If a Game Object, it must have a texture, such as a Sprite.
     */
    setBitmap: function (maskObject)
    {
        this.bitmapMask = maskObject;
    },

    /**
     * Prepares the WebGL Renderer to render a Game Object with this mask applied.
     *
     * This renders the masking Game Object to the mask framebuffer and switches to the main framebuffer so that the masked Game Object will be rendered to it instead of being rendered directly to the frame.
     *
     * @method Phaser.Display.Masks.BitmapMask#preRenderWebGL
     * @since 3.0.0
     *
     * @param {(Phaser.Renderer.Canvas.CanvasRenderer|Phaser.Renderer.WebGL.WebGLRenderer)} renderer - The WebGL Renderer to prepare.
     * @param {Phaser.GameObjects.GameObject} maskedObject - The masked Game Object which will be drawn.
     * @param {Phaser.Cameras.Scene2D.Camera} camera - The Camera to render to.
     */
    preRenderWebGL: function (renderer, maskedObject, camera)
    {
        renderer.pipelines.BITMAPMASK_PIPELINE.beginMask(this, maskedObject, camera);
    },

    /**
     * Finalizes rendering of a masked Game Object.
     *
     * This resets the previously bound framebuffer and switches the WebGL Renderer to the Bitmap Mask Pipeline, which uses a special fragment shader to apply the masking effect.
     *
     * @method Phaser.Display.Masks.BitmapMask#postRenderWebGL
     * @since 3.0.0
     *
     * @param {(Phaser.Renderer.Canvas.CanvasRenderer|Phaser.Renderer.WebGL.WebGLRenderer)} renderer - The WebGL Renderer to clean up.
     * @param {Phaser.Cameras.Scene2D.Camera} camera - The Camera to render to.
     * @param {Phaser.Renderer.WebGL.RenderTarget} [renderTarget] - Optional WebGL RenderTarget.
     */
    postRenderWebGL: function (renderer, camera, renderTarget)
    {
        renderer.pipelines.BITMAPMASK_PIPELINE.endMask(this, camera, renderTarget);
    },

    /**
     * This is a NOOP method. Bitmap Masks are not supported by the Canvas Renderer.
     *
     * @method Phaser.Display.Masks.BitmapMask#preRenderCanvas
     * @since 3.0.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.
     */
    preRenderCanvas: function ()
    {
        // NOOP
    },

    /**
     * This is a NOOP method. Bitmap Masks are not supported by the Canvas Renderer.
     *
     * @method Phaser.Display.Masks.BitmapMask#postRenderCanvas
     * @since 3.0.0
     *
     * @param {(Phaser.Renderer.Canvas.CanvasRenderer|Phaser.Renderer.WebGL.WebGLRenderer)} renderer - The Canvas Renderer which would be rendered to.
     */
    postRenderCanvas: function ()
    {
        // NOOP
    },

    /**
     * Destroys this BitmapMask and nulls any references it holds.
     *
     * Note that if a Game Object is currently using this mask it will _not_ automatically detect you have destroyed it,
     * so be sure to call `clearMask` on any Game Object using it, before destroying it.
     *
     * @method Phaser.Display.Masks.BitmapMask#destroy
     * @since 3.7.0
     */
    destroy: function ()
    {
        this.bitmapMask = null;
    }

});

/**
 * A Bitmap Mask combines the alpha (opacity) of a masked pixel with the alpha of another pixel.
 * Unlike the Geometry Mask, which is a clipping path, a Bitmap Mask behaves like an alpha mask,
 * not a clipping path. It is only available when using the WebGL Renderer.
 *
 * A Bitmap Mask can use any Game Object, or Dynamic Texture, to determine the alpha of each pixel of the masked Game Object(s).
 * For any given point of a masked Game Object's texture, the pixel's alpha will be multiplied by the alpha
 * of the pixel at the same position in the Bitmap Mask's Game Object. The color of the pixel from the
 * Bitmap Mask doesn't matter.
 *
 * For example, if a pure blue pixel with an alpha of 0.95 is masked with a pure red pixel with an
 * alpha of 0.5, the resulting pixel will be pure blue with an alpha of 0.475. Naturally, this means
 * that a pixel in the mask with an alpha of 0 will hide the corresponding pixel in all masked Game Objects
 *  A pixel with an alpha of 1 in the masked Game Object will receive the same alpha as the
 * corresponding pixel in the mask.
 *
 * Note: You cannot combine Bitmap Masks and Blend Modes on the same Game Object. You can, however,
 * combine Geometry Masks and Blend Modes together.
 *
 * The Bitmap Mask's location matches the location of its Game Object, not the location of the
 * masked objects. Moving or transforming the underlying Game Object will change the mask
 * (and affect the visibility of any masked objects), whereas moving or transforming a masked object
 * will not affect the mask.
 *
 * The Bitmap Mask will not render its Game Object by itself. If the Game Object is not in a
 * Scene's display list, it will only be used for the mask and its full texture will not be directly
 * visible. Adding the underlying Game Object to a Scene will not cause any problems - it will
 * render as a normal Game Object and will also serve as a mask.
 *
 * @method Phaser.GameObjects.GameObjectFactory#bitmapMask
 * @since 3.60.0
 *
 * @param {(Phaser.GameObjects.GameObject|Phaser.Textures.DynamicTexture)} [maskObject] - The Game Object or Texture that will be used as the mask. If `null` it will generate an Image Game Object using the rest of the arguments.
 * @param {number} [x] - If creating a Game Object, the horizontal position in the world.
 * @param {number} [y] - If creating a Game Object, the vertical position in the world.
 * @param {(string|Phaser.Textures.Texture)} [texture] - If creating a Game Object, the key, or instance of the Texture it will use to render with, as stored in the Texture Manager.
 * @param {(string|number|Phaser.Textures.Frame)} [frame] - If creating a Game Object, an optional frame from the Texture this Game Object is rendering with.
 *
 * @return {Phaser.Display.Masks.BitmapMask} The Bitmap Mask that was created.
 */
GameObjectFactory.register('bitmapMask', function (maskObject, x, y, key, frame)
{
    return new BitmapMask(this.scene, maskObject, x, y, key, frame);
});

module.exports = BitmapMask;


/**
 * @typedef {object} Phaser.Types.GameObjects.BitmapText.JSONBitmapText
 * @extends {Phaser.Types.GameObjects.JSONGameObject}
 * @since 3.0.0
 *
 * @property {string} font - The name of the font.
 * @property {string} text - The text that this Bitmap Text displays.
 * @property {number} fontSize - The size of the font.
 * @property {number} letterSpacing - Adds / Removes spacing between characters.
 * @property {number} lineSpacing - Adds / Removes spacing between lines in multi-line text.
 * @property {number} align - The alignment of the text in a multi-line BitmapText object.
 */