{"version":3,"file":"ScissorSystem.mjs","sources":["../../src/mask/ScissorSystem.ts"],"sourcesContent":["import { extensions, ExtensionType } from '@pixi/extensions';\nimport { Matrix, Rectangle } from '@pixi/math';\nimport { settings } from '@pixi/settings';\nimport { AbstractMaskSystem } from './AbstractMaskSystem';\n\nimport type { ExtensionMetadata } from '@pixi/extensions';\nimport type { Renderer } from '../Renderer';\nimport type { MaskData } from './MaskData';\n\nconst tempMatrix = new Matrix();\nconst rectPool: Rectangle[] = [];\n\n/**\n * System plugin to the renderer to manage scissor masking.\n *\n * Scissor masking discards pixels outside of a rectangle called the scissor box. The scissor box is in the framebuffer\n * viewport's space; however, the mask's rectangle is projected from world-space to viewport space automatically\n * by this system.\n * @memberof PIXI\n */\nexport class ScissorSystem extends AbstractMaskSystem\n{\n    /** @ignore */\n    static extension: ExtensionMetadata = {\n        type: ExtensionType.RendererSystem,\n        name: 'scissor',\n    };\n\n    /**\n     * @param {PIXI.Renderer} renderer - The renderer this System works for.\n     */\n    constructor(renderer: Renderer)\n    {\n        super(renderer);\n\n        this.glConst = settings.ADAPTER.getWebGLRenderingContext().SCISSOR_TEST;\n    }\n\n    getStackLength(): number\n    {\n        const maskData = this.maskStack[this.maskStack.length - 1];\n\n        if (maskData)\n        {\n            return maskData._scissorCounter;\n        }\n\n        return 0;\n    }\n\n    /**\n     * evaluates _boundsTransformed, _scissorRect for MaskData\n     * @param maskData\n     */\n    calcScissorRect(maskData: MaskData): void\n    {\n        if (maskData._scissorRectLocal)\n        {\n            return;\n        }\n\n        const prevData = maskData._scissorRect;\n        const { maskObject } = maskData;\n        const { renderer } = this;\n        const renderTextureSystem = renderer.renderTexture;\n        const rect = maskObject.getBounds(true, rectPool.pop() ?? new Rectangle());\n\n        this.roundFrameToPixels(rect,\n            renderTextureSystem.current ? renderTextureSystem.current.resolution : renderer.resolution,\n            renderTextureSystem.sourceFrame,\n            renderTextureSystem.destinationFrame,\n            renderer.projection.transform);\n\n        if (prevData)\n        {\n            rect.fit(prevData);\n        }\n        maskData._scissorRectLocal = rect;\n    }\n\n    private static isMatrixRotated(matrix: Matrix)\n    {\n        if (!matrix)\n        {\n            return false;\n        }\n        const { a, b, c, d } = matrix;\n\n        // Skip if skew/rotation present in matrix, except for multiple of 90° rotation. If rotation\n        // is a multiple of 90°, then either pair of (b,c) or (a,d) will be (0,0).\n        return ((Math.abs(b) > 1e-4 || Math.abs(c) > 1e-4)\n            && (Math.abs(a) > 1e-4 || Math.abs(d) > 1e-4));\n    }\n\n    /**\n     * Test, whether the object can be scissor mask with current renderer projection.\n     * Calls \"calcScissorRect()\" if its true.\n     * @param maskData - mask data\n     * @returns whether Whether the object can be scissor mask\n     */\n    public testScissor(maskData: MaskData): boolean\n    {\n        const { maskObject } = maskData;\n\n        if (!maskObject.isFastRect || !maskObject.isFastRect())\n        {\n            return false;\n        }\n        if (ScissorSystem.isMatrixRotated(maskObject.worldTransform))\n        {\n            return false;\n        }\n        if (ScissorSystem.isMatrixRotated(this.renderer.projection.transform))\n        {\n            return false;\n        }\n\n        this.calcScissorRect(maskData);\n\n        const rect = maskData._scissorRectLocal;\n\n        return rect.width > 0 && rect.height > 0;\n    }\n\n    private roundFrameToPixels(\n        frame: Rectangle,\n        resolution: number,\n        bindingSourceFrame: Rectangle,\n        bindingDestinationFrame: Rectangle,\n        transform?: Matrix,\n    )\n    {\n        if (ScissorSystem.isMatrixRotated(transform))\n        {\n            return;\n        }\n\n        transform = transform ? tempMatrix.copyFrom(transform) : tempMatrix.identity();\n\n        // Get forward transform from world space to screen space\n        transform\n            .translate(-bindingSourceFrame.x, -bindingSourceFrame.y)\n            .scale(\n                bindingDestinationFrame.width / bindingSourceFrame.width,\n                bindingDestinationFrame.height / bindingSourceFrame.height)\n            .translate(bindingDestinationFrame.x, bindingDestinationFrame.y);\n\n        // Convert frame to screen space\n        (this.renderer.filter as any).transformAABB(transform, frame);\n\n        frame.fit(bindingDestinationFrame);\n        frame.x = Math.round(frame.x * resolution);\n        frame.y = Math.round(frame.y * resolution);\n        frame.width = Math.round(frame.width * resolution);\n        frame.height = Math.round(frame.height * resolution);\n    }\n\n    /**\n     * Applies the Mask and adds it to the current stencil stack.\n     * @author alvin\n     * @param maskData - The mask data.\n     */\n    push(maskData: MaskData): void\n    {\n        if (!maskData._scissorRectLocal)\n        {\n            this.calcScissorRect(maskData);\n        }\n\n        const { gl } = this.renderer;\n\n        if (!maskData._scissorRect)\n        {\n            gl.enable(gl.SCISSOR_TEST);\n        }\n\n        maskData._scissorCounter++;\n        maskData._scissorRect = maskData._scissorRectLocal;\n        this._useCurrent();\n    }\n\n    /**\n     * This should be called after a mask is popped off the mask stack. It will rebind the scissor box to be latest with the\n     * last mask in the stack.\n     *\n     * This can also be called when you directly modify the scissor box and want to restore PixiJS state.\n     * @param maskData - The mask data.\n     */\n    pop(maskData?: MaskData): void\n    {\n        const { gl } = this.renderer;\n\n        if (maskData)\n        {\n            rectPool.push(maskData._scissorRectLocal);\n        }\n\n        if (this.getStackLength() > 0)\n        {\n            this._useCurrent();\n        }\n        else\n        {\n            gl.disable(gl.SCISSOR_TEST);\n        }\n    }\n\n    /**\n     * Setup renderer to use the current scissor data.\n     * @private\n     */\n    _useCurrent(): void\n    {\n        const rect = this.maskStack[this.maskStack.length - 1]._scissorRect;\n        let y: number;\n\n        if (this.renderer.renderTexture.current)\n        {\n            y = rect.y;\n        }\n        else\n        {\n            // flipY. In future we'll have it over renderTextures as an option\n            y = this.renderer.height - rect.height - rect.y;\n        }\n\n        this.renderer.gl.scissor(rect.x, y, rect.width, rect.height);\n    }\n}\n\nextensions.add(ScissorSystem);\n"],"names":["_ScissorSystem"],"mappings":"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"}