{"version":3,"file":"BlendModePipe.mjs","sources":["../../../../../src/rendering/renderers/shared/blendModes/BlendModePipe.ts"],"sourcesContent":["import { extensions, ExtensionType } from '../../../../extensions/Extensions';\nimport { FilterEffect } from '../../../../filters/FilterEffect';\nimport { RenderGroup } from '../../../../scene/container/RenderGroup';\nimport { warn } from '../../../../utils/logging/warn';\n\nimport type { BlendModeFilter } from '../../../../filters/blend-modes/BlendModeFilter';\nimport type { FilterInstruction } from '../../../../filters/FilterSystem';\nimport type { Renderer } from '../../types';\nimport type { Instruction } from '../instructions/Instruction';\nimport type { InstructionSet } from '../instructions/InstructionSet';\nimport type { InstructionPipe } from '../instructions/RenderPipe';\nimport type { Renderable } from '../Renderable';\nimport type { BLEND_MODES } from '../state/const';\n\ninterface AdvancedBlendInstruction extends Instruction\n{\n    renderPipeId: 'blendMode',\n    blendMode: BLEND_MODES,\n    activeBlend: Renderable[],\n}\n\n// class map\nconst BLEND_MODE_FILTERS: Partial<Record<BLEND_MODES, new () => BlendModeFilter>> = {} as const;\n\nextensions.handle(ExtensionType.BlendMode, (value) =>\n{\n    if (!value.name)\n    {\n        throw new Error('BlendMode extension must have a name property');\n    }\n    BLEND_MODE_FILTERS[value.name as BLEND_MODES] = value.ref;\n}, (value) =>\n{\n    delete BLEND_MODE_FILTERS[value.name as BLEND_MODES];\n});\n\n/**\n * This Pipe handles the blend mode switching of the renderer.\n * It will insert instructions into the {@link InstructionSet} to switch the blend mode according to the\n * blend modes of the scene graph.\n *\n * This pipe is were wwe handle Advanced blend modes. Advanced blend modes essentially wrap the renderables\n * in a filter that applies the blend mode.\n *\n * You only need to use this class if you are building your own render instruction set rather than letting PixiJS build\n * the instruction set for you by traversing the scene graph\n * @category rendering\n * @internal\n */\nexport class BlendModePipe implements InstructionPipe<AdvancedBlendInstruction>\n{\n    /** @ignore */\n    public static extension = {\n        type: [\n            ExtensionType.WebGLPipes,\n            ExtensionType.WebGPUPipes,\n            ExtensionType.CanvasPipes,\n        ],\n        name: 'blendMode',\n    } as const;\n\n    private _renderer: Renderer;\n\n    private _renderableList?: Renderable[];\n    private _activeBlendMode: BLEND_MODES;\n    private readonly _blendModeStack: BLEND_MODES[] = [];\n\n    private _isAdvanced = false;\n\n    private _filterHash: Partial<Record<BLEND_MODES, FilterEffect>> = Object.create(null);\n\n    constructor(renderer: Renderer)\n    {\n        this._renderer = renderer;\n        this._renderer.runners.prerender.add(this);\n    }\n\n    public prerender()\n    {\n        // make sure we reset the blend modes to normal\n        // this way the next render will register any changes\n        this._activeBlendMode = 'normal';\n        this._isAdvanced = false;\n    }\n\n    /**\n     * Push a blend mode onto the internal stack and apply it to the instruction set if needed.\n     * @param renderable - The renderable or {@link RenderGroup} associated with the change.\n     * @param blendMode - The blend mode to activate.\n     * @param instructionSet - The instruction set being built.\n     */\n    public pushBlendMode(renderable: Renderable | RenderGroup, blendMode: BLEND_MODES, instructionSet: InstructionSet): void\n    {\n        this._blendModeStack.push(blendMode);\n\n        this.setBlendMode(renderable, blendMode, instructionSet);\n    }\n\n    /**\n     * Pop the last blend mode from the stack and apply the new top-of-stack mode.\n     * @param instructionSet - The instruction set being built.\n     */\n    public popBlendMode(instructionSet: InstructionSet): void\n    {\n        this._blendModeStack.pop();\n        const blendMode = this._blendModeStack[this._activeBlendMode.length - 1] ?? 'normal';\n\n        this.setBlendMode(null, blendMode, instructionSet);\n    }\n\n    /**\n     * Ensure a blend mode switch is added to the instruction set when the mode changes.\n     * If an advanced blend mode is active, subsequent renderables will be collected so they can be\n     * rendered within a single filter pass.\n     * @param renderable - The renderable or {@link RenderGroup} to associate with the change, or null when unwinding.\n     * @param blendMode - The target blend mode.\n     * @param instructionSet - The instruction set being built.\n     */\n    public setBlendMode(\n        renderable: Renderable | RenderGroup | null,\n        blendMode: BLEND_MODES,\n        instructionSet: InstructionSet\n    )\n    {\n        const isRenderGroup = renderable instanceof RenderGroup;\n\n        if (this._activeBlendMode === blendMode)\n        {\n            if (this._isAdvanced && renderable && !isRenderGroup)\n            {\n                this._renderableList?.push(renderable);\n            }\n\n            return;\n        }\n\n        if (this._isAdvanced) this._endAdvancedBlendMode(instructionSet);\n\n        this._activeBlendMode = blendMode;\n\n        if (!renderable) return;\n\n        this._isAdvanced = !!BLEND_MODE_FILTERS[blendMode];\n\n        if (this._isAdvanced) this._beginAdvancedBlendMode(renderable, instructionSet);\n    }\n\n    private _beginAdvancedBlendMode(renderable: Renderable | RenderGroup, instructionSet: InstructionSet)\n    {\n        this._renderer.renderPipes.batch.break(instructionSet);\n\n        const blendMode = this._activeBlendMode;\n\n        if (!BLEND_MODE_FILTERS[blendMode])\n        {\n            // #if _DEBUG\n            warn(`Unable to assign BlendMode: '${blendMode}'. `\n                + `You may want to include: import 'pixi.js/advanced-blend-modes'`);\n            // #endif\n\n            return;\n        }\n\n        const filterEffect = this._ensureFilterEffect(blendMode);\n        const isRenderGroup = renderable instanceof RenderGroup;\n        const instruction: FilterInstruction = {\n            renderPipeId: 'filter',\n            action: 'pushFilter',\n            filterEffect,\n            renderables: isRenderGroup ? null : [renderable],\n            container: isRenderGroup ? renderable.root : null,\n            canBundle: false\n        };\n\n        this._renderableList = instruction.renderables;\n\n        instructionSet.add(instruction);\n    }\n\n    private _ensureFilterEffect(blendMode: BLEND_MODES): FilterEffect\n    {\n        let filterEffect: FilterEffect = this._filterHash[blendMode];\n\n        if (!filterEffect)\n        {\n            filterEffect = this._filterHash[blendMode] = new FilterEffect();\n            filterEffect.filters = [new BLEND_MODE_FILTERS[blendMode as keyof typeof BLEND_MODE_FILTERS]()];\n        }\n\n        return filterEffect;\n    }\n\n    private _endAdvancedBlendMode(instructionSet: InstructionSet)\n    {\n        this._isAdvanced = false;\n        this._renderableList = null;\n        this._renderer.renderPipes.batch.break(instructionSet);\n\n        instructionSet.add({\n            renderPipeId: 'filter',\n            action: 'popFilter',\n            canBundle: false,\n        });\n    }\n\n    /**\n     * called when the instruction build process is starting this will reset internally to the default blend mode\n     * @internal\n     */\n    public buildStart()\n    {\n        this._isAdvanced = false;\n    }\n\n    /**\n     * called when the instruction build process is finished, ensuring that if there is an advanced blend mode\n     * active, we add the final render instructions added to the instruction set\n     * @param instructionSet - The instruction set we are adding to\n     * @internal\n     */\n    public buildEnd(instructionSet: InstructionSet)\n    {\n        if (!this._isAdvanced) return;\n\n        this._endAdvancedBlendMode(instructionSet);\n    }\n\n    /** @internal */\n    public destroy()\n    {\n        this._renderer = null;\n        this._renderableList = null;\n\n        for (const i in this._filterHash)\n        {\n            this._filterHash[i as BLEND_MODES].destroy();\n        }\n\n        this._filterHash = null;\n    }\n}\n"],"names":[],"mappings":"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"}