{"version":3,"file":"GifSource.mjs","sources":["../../src/gif/GifSource.ts"],"sourcesContent":["import { decompressFrames, type ParsedFrame, parseGIF } from 'gifuct-js';\nimport { DOMAdapter } from '../environment/adapter';\nimport { CanvasSource, type CanvasSourceOptions } from '../rendering/renderers/shared/texture/sources/CanvasSource';\nimport { Texture } from '../rendering/renderers/shared/texture/Texture';\n\n/**\n * Represents a single frame of a GIF. Includes image and timing data.\n * @category gif\n * @advanced\n */\ninterface GifFrame\n{\n    /** Image data for the current frame */\n    texture: Texture<CanvasSource>;\n    /** The start of the current frame, in milliseconds */\n    start: number;\n    /** The end of the current frame, in milliseconds */\n    end: number;\n}\n\n/**\n * Options when constructing from buffer\n * @category gif\n * @advanced\n */\ninterface GifBufferOptions extends Omit<CanvasSourceOptions, 'resource'>\n{\n    /** FPS to use when the GIF animation doesn't define any delay between frames */\n    fps?: number;\n}\n\n/**\n * Resource provided to GifSprite instances. This is very similar to using a shared\n * Texture between Sprites. This source contains all the frames and animation needed\n * to support playback.\n * @category gif\n * @advanced\n */\nclass GifSource\n{\n    /** Width of the animation */\n    public readonly width: number;\n\n    /** Height of the animation */\n    public readonly height: number;\n\n    /** The total time to play the animation in milliseconds */\n    public readonly duration: number;\n\n    /** Animation frames */\n    public readonly frames: GifFrame[];\n\n    /** Textures */\n    public readonly textures: Texture<CanvasSource>[];\n\n    /** Total number of frames in the animation */\n    public readonly totalFrames: number;\n\n    /**\n     * @param frames - Array of GifFrame instances.\n     */\n    constructor(frames: GifFrame[])\n    {\n        // #if _DEBUG\n        if (!frames || !frames.length) throw new Error('Invalid frames');\n        // #endif\n\n        // All frames are the same size, get the first frame's size\n        const [{ texture: { width, height } }] = frames;\n\n        this.width = width;\n        this.height = height;\n        this.frames = frames;\n        this.textures = this.frames.map((frame) => frame.texture);\n        this.totalFrames = this.frames.length;\n        this.duration = this.frames[this.totalFrames - 1].end;\n    }\n\n    /** Destroy animation data and don't use after this */\n    public destroy()\n    {\n        for (const texture of this.textures)\n        {\n            texture.destroy(true);\n        }\n        for (const frame of this.frames)\n        {\n            frame.texture = null;\n        }\n        this.frames.length = 0;\n        this.textures.length = 0;\n        Object.assign(this, {\n            frames: null,\n            textures: null,\n            width: 0,\n            height: 0,\n            duration: 0,\n            totalFrames: 0,\n        });\n    }\n\n    /**\n     * Create an animated GIF animation from a GIF image's ArrayBuffer. The easiest way to get\n     * the buffer is to use Assets.\n     * @example\n     * import { GifSource, GifSprite } from 'pixi.js/gif';\n     *\n     * const buffer = await fetch('./file.gif').then(res => res.arrayBuffer());\n     * const source = GifSource.from(buffer);\n     * const sprite = new GifSprite(source);\n     * @param buffer - GIF image arraybuffer from Assets.\n     * @param options - Optional options to use when building from buffer.\n     */\n    public static from(buffer: ArrayBuffer, options?: GifBufferOptions): GifSource\n    {\n        if (!buffer || buffer.byteLength === 0)\n        {\n            throw new Error('Invalid buffer');\n        }\n\n        // fix https://github.com/matt-way/gifuct-js/issues/30\n        const validateAndFix = (gif: any): void =>\n        {\n            let currentGce = null;\n\n            for (const frame of gif.frames)\n            {\n                currentGce = frame.gce ?? currentGce;\n\n                // fix loosing graphic control extension for same frames\n                if ('image' in frame && !('gce' in frame))\n                {\n                    frame.gce = currentGce;\n                }\n            }\n        };\n\n        const gif = parseGIF(buffer);\n\n        validateAndFix(gif);\n        const gifFrames = decompressFrames(gif, true);\n        const frames: GifFrame[] = [];\n        const animWidth = gif.lsd.width;\n        const animHeight = gif.lsd.height;\n\n        // Temporary canvases required for compositing frames\n        const canvas = DOMAdapter.get().createCanvas(animWidth, animHeight);\n        const context = canvas.getContext('2d', { willReadFrequently: true });\n        const patchCanvas = DOMAdapter.get().createCanvas();\n        const patchContext = patchCanvas.getContext('2d');\n\n        let time = 0;\n        let previousFrame: ImageData | null = null;\n\n        // Some GIFs have a non-zero frame delay, so we need to calculate the fallback\n        const { fps = 30, ...canvasSourceOptions } = options ?? {};\n        const defaultDelay = 1000 / fps;\n\n        // Precompute each frame and store as ImageData\n        for (let i = 0; i < gifFrames.length; i++)\n        {\n            // Some GIF's omit the disposalType, so let's assume clear if missing\n            const {\n                disposalType = 2,\n                delay = defaultDelay,\n                patch,\n                dims: { width, height, left, top },\n            } = gifFrames[i] as ParsedFrame;\n\n            patchCanvas.width = width;\n            patchCanvas.height = height;\n            patchContext.clearRect(0, 0, width, height);\n            const patchData = patchContext.createImageData(width, height);\n\n            patchData.data.set(patch);\n            patchContext.putImageData(patchData, 0, 0);\n\n            if (disposalType === 3)\n            {\n                previousFrame = context.getImageData(0, 0, animWidth, animHeight);\n            }\n\n            context.drawImage(patchCanvas as CanvasImageSource, left, top);\n            const imageData = context.getImageData(0, 0, animWidth, animHeight);\n\n            if (disposalType === 2)\n            {\n                context.clearRect(0, 0, animWidth, animHeight);\n            }\n            else if (disposalType === 3)\n            {\n                context.putImageData(previousFrame as ImageData, 0, 0);\n            }\n\n            // Create new texture\n            const resource = DOMAdapter.get().createCanvas(\n                imageData.width,\n                imageData.height\n            ) as HTMLCanvasElement;\n            const resourceContext = resource.getContext('2d');\n\n            resourceContext.putImageData(imageData, 0, 0);\n\n            frames.push({\n                start: time,\n                end: time + delay,\n                texture: new Texture({\n                    source: new CanvasSource({\n                        resource,\n                        ...canvasSourceOptions,\n                    }),\n                }),\n            });\n            time += delay;\n        }\n\n        // clear the canvases\n        canvas.width = canvas.height = 0;\n        patchCanvas.width = patchCanvas.height = 0;\n\n        return new GifSource(frames);\n    }\n}\n\nexport { GifBufferOptions, GifFrame, GifSource };\n"],"names":["gif"],"mappings":";;;;;;AAsCA,MAAM,SAAA,CACN;AAAA;AAAA;AAAA;AAAA,EAsBI,YAAY,MAAA,EACZ;AAEI,IAAA,IAAI,CAAC,UAAU,CAAC,MAAA,CAAO,QAAQ,MAAM,IAAI,MAAM,gBAAgB,CAAA;AAI/D,IAAA,MAAM,CAAC,EAAE,OAAA,EAAS,EAAE,OAAO,MAAA,EAAO,EAAG,CAAA,GAAI,MAAA;AAEzC,IAAA,IAAA,CAAK,KAAA,GAAQ,KAAA;AACb,IAAA,IAAA,CAAK,MAAA,GAAS,MAAA;AACd,IAAA,IAAA,CAAK,MAAA,GAAS,MAAA;AACd,IAAA,IAAA,CAAK,WAAW,IAAA,CAAK,MAAA,CAAO,IAAI,CAAC,KAAA,KAAU,MAAM,OAAO,CAAA;AACxD,IAAA,IAAA,CAAK,WAAA,GAAc,KAAK,MAAA,CAAO,MAAA;AAC/B,IAAA,IAAA,CAAK,WAAW,IAAA,CAAK,MAAA,CAAO,IAAA,CAAK,WAAA,GAAc,CAAC,CAAA,CAAE,GAAA;AAAA,EACtD;AAAA;AAAA,EAGO,OAAA,GACP;AACI,IAAA,KAAA,MAAW,OAAA,IAAW,KAAK,QAAA,EAC3B;AACI,MAAA,OAAA,CAAQ,QAAQ,IAAI,CAAA;AAAA,IACxB;AACA,IAAA,KAAA,MAAW,KAAA,IAAS,KAAK,MAAA,EACzB;AACI,MAAA,KAAA,CAAM,OAAA,GAAU,IAAA;AAAA,IACpB;AACA,IAAA,IAAA,CAAK,OAAO,MAAA,GAAS,CAAA;AACrB,IAAA,IAAA,CAAK,SAAS,MAAA,GAAS,CAAA;AACvB,IAAA,MAAA,CAAO,OAAO,IAAA,EAAM;AAAA,MAChB,MAAA,EAAQ,IAAA;AAAA,MACR,QAAA,EAAU,IAAA;AAAA,MACV,KAAA,EAAO,CAAA;AAAA,MACP,MAAA,EAAQ,CAAA;AAAA,MACR,QAAA,EAAU,CAAA;AAAA,MACV,WAAA,EAAa;AAAA,KAChB,CAAA;AAAA,EACL;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAcA,OAAc,IAAA,CAAK,MAAA,EAAqB,OAAA,EACxC;AACI,IAAA,IAAI,CAAC,MAAA,IAAU,MAAA,CAAO,UAAA,KAAe,CAAA,EACrC;AACI,MAAA,MAAM,IAAI,MAAM,gBAAgB,CAAA;AAAA,IACpC;AAGA,IAAA,MAAM,cAAA,GAAiB,CAACA,IAAAA,KACxB;AACI,MAAA,IAAI,UAAA,GAAa,IAAA;AAEjB,MAAA,KAAA,MAAW,KAAA,IAASA,KAAI,MAAA,EACxB;AACI,QAAA,UAAA,GAAa,MAAM,GAAA,IAAO,UAAA;AAG1B,QAAA,IAAI,OAAA,IAAW,KAAA,IAAS,EAAE,KAAA,IAAS,KAAA,CAAA,EACnC;AACI,UAAA,KAAA,CAAM,GAAA,GAAM,UAAA;AAAA,QAChB;AAAA,MACJ;AAAA,IACJ,CAAA;AAEA,IAAA,MAAM,GAAA,GAAM,SAAS,MAAM,CAAA;AAE3B,IAAA,cAAA,CAAe,GAAG,CAAA;AAClB,IAAA,MAAM,SAAA,GAAY,gBAAA,CAAiB,GAAA,EAAK,IAAI,CAAA;AAC5C,IAAA,MAAM,SAAqB,EAAC;AAC5B,IAAA,MAAM,SAAA,GAAY,IAAI,GAAA,CAAI,KAAA;AAC1B,IAAA,MAAM,UAAA,GAAa,IAAI,GAAA,CAAI,MAAA;AAG3B,IAAA,MAAM,SAAS,UAAA,CAAW,GAAA,EAAI,CAAE,YAAA,CAAa,WAAW,UAAU,CAAA;AAClE,IAAA,MAAM,UAAU,MAAA,CAAO,UAAA,CAAW,MAAM,EAAE,kBAAA,EAAoB,MAAM,CAAA;AACpE,IAAA,MAAM,WAAA,GAAc,UAAA,CAAW,GAAA,EAAI,CAAE,YAAA,EAAa;AAClD,IAAA,MAAM,YAAA,GAAe,WAAA,CAAY,UAAA,CAAW,IAAI,CAAA;AAEhD,IAAA,IAAI,IAAA,GAAO,CAAA;AACX,IAAA,IAAI,aAAA,GAAkC,IAAA;AAGtC,IAAA,MAAM,EAAE,GAAA,GAAM,EAAA,EAAI,GAAG,mBAAA,EAAoB,GAAI,WAAW,EAAC;AACzD,IAAA,MAAM,eAAe,GAAA,GAAO,GAAA;AAG5B,IAAA,KAAA,IAAS,CAAA,GAAI,CAAA,EAAG,CAAA,GAAI,SAAA,CAAU,QAAQ,CAAA,EAAA,EACtC;AAEI,MAAA,MAAM;AAAA,QACF,YAAA,GAAe,CAAA;AAAA,QACf,KAAA,GAAQ,YAAA;AAAA,QACR,KAAA;AAAA,QACA,IAAA,EAAM,EAAE,KAAA,EAAO,MAAA,EAAQ,MAAM,GAAA;AAAI,OACrC,GAAI,UAAU,CAAC,CAAA;AAEf,MAAA,WAAA,CAAY,KAAA,GAAQ,KAAA;AACpB,MAAA,WAAA,CAAY,MAAA,GAAS,MAAA;AACrB,MAAA,YAAA,CAAa,SAAA,CAAU,CAAA,EAAG,CAAA,EAAG,KAAA,EAAO,MAAM,CAAA;AAC1C,MAAA,MAAM,SAAA,GAAY,YAAA,CAAa,eAAA,CAAgB,KAAA,EAAO,MAAM,CAAA;AAE5D,MAAA,SAAA,CAAU,IAAA,CAAK,IAAI,KAAK,CAAA;AACxB,MAAA,YAAA,CAAa,YAAA,CAAa,SAAA,EAAW,CAAA,EAAG,CAAC,CAAA;AAEzC,MAAA,IAAI,iBAAiB,CAAA,EACrB;AACI,QAAA,aAAA,GAAgB,OAAA,CAAQ,YAAA,CAAa,CAAA,EAAG,CAAA,EAAG,WAAW,UAAU,CAAA;AAAA,MACpE;AAEA,MAAA,OAAA,CAAQ,SAAA,CAAU,WAAA,EAAkC,IAAA,EAAM,GAAG,CAAA;AAC7D,MAAA,MAAM,YAAY,OAAA,CAAQ,YAAA,CAAa,CAAA,EAAG,CAAA,EAAG,WAAW,UAAU,CAAA;AAElE,MAAA,IAAI,iBAAiB,CAAA,EACrB;AACI,QAAA,OAAA,CAAQ,SAAA,CAAU,CAAA,EAAG,CAAA,EAAG,SAAA,EAAW,UAAU,CAAA;AAAA,MACjD,CAAA,MAAA,IACS,iBAAiB,CAAA,EAC1B;AACI,QAAA,OAAA,CAAQ,YAAA,CAAa,aAAA,EAA4B,CAAA,EAAG,CAAC,CAAA;AAAA,MACzD;AAGA,MAAA,MAAM,QAAA,GAAW,UAAA,CAAW,GAAA,EAAI,CAAE,YAAA;AAAA,QAC9B,SAAA,CAAU,KAAA;AAAA,QACV,SAAA,CAAU;AAAA,OACd;AACA,MAAA,MAAM,eAAA,GAAkB,QAAA,CAAS,UAAA,CAAW,IAAI,CAAA;AAEhD,MAAA,eAAA,CAAgB,YAAA,CAAa,SAAA,EAAW,CAAA,EAAG,CAAC,CAAA;AAE5C,MAAA,MAAA,CAAO,IAAA,CAAK;AAAA,QACR,KAAA,EAAO,IAAA;AAAA,QACP,KAAK,IAAA,GAAO,KAAA;AAAA,QACZ,OAAA,EAAS,IAAI,OAAA,CAAQ;AAAA,UACjB,MAAA,EAAQ,IAAI,YAAA,CAAa;AAAA,YACrB,QAAA;AAAA,YACA,GAAG;AAAA,WACN;AAAA,SACJ;AAAA,OACJ,CAAA;AACD,MAAA,IAAA,IAAQ,KAAA;AAAA,IACZ;AAGA,IAAA,MAAA,CAAO,KAAA,GAAQ,OAAO,MAAA,GAAS,CAAA;AAC/B,IAAA,WAAA,CAAY,KAAA,GAAQ,YAAY,MAAA,GAAS,CAAA;AAEzC,IAAA,OAAO,IAAI,UAAU,MAAM,CAAA;AAAA,EAC/B;AACJ;;;;"}