{"version":3,"file":"CanvasMaskSystem.mjs","sources":["../src/CanvasMaskSystem.ts"],"sourcesContent":["import { extensions, ExtensionType, SHAPES } from '@pixi/core';\n\nimport type { ExtensionMetadata, ISystem, MaskData, Polygon } from '@pixi/core';\nimport type { Container } from '@pixi/display';\nimport type { Graphics } from '@pixi/graphics';\nimport type { CanvasRenderer } from './CanvasRenderer';\n/**\n * A set of functions used to handle masking.\n *\n * Sprite masking is not supported on the CanvasRenderer.\n * @class\n * @memberof PIXI\n */\nexport class CanvasMaskSystem implements ISystem\n{\n    /** @ignore */\n    static extension: ExtensionMetadata = {\n        type:  ExtensionType.CanvasRendererSystem,\n        name: 'mask',\n    };\n\n    /** A reference to the current renderer */\n    private renderer: CanvasRenderer;\n    private _foundShapes: Array<Graphics> = [];\n\n    /** @param renderer - A reference to the current renderer */\n    constructor(renderer: CanvasRenderer)\n    {\n        this.renderer = renderer;\n    }\n\n    /**\n     * This method adds it to the current stack of masks.\n     * @param maskData - the maskData that will be pushed\n     */\n    pushMask(maskData: MaskData | Graphics): void\n    {\n        const renderer = this.renderer;\n        const maskObject = ((maskData as MaskData).maskObject || maskData) as Container;\n\n        renderer.canvasContext.activeContext.save();\n\n        // TODO support sprite alpha masks??\n        // lots of effort required. If demand is great enough..\n\n        const foundShapes = this._foundShapes;\n\n        this.recursiveFindShapes(maskObject, foundShapes);\n        if (foundShapes.length > 0)\n        {\n            const context = renderer.canvasContext.activeContext;\n\n            context.beginPath();\n\n            for (let i = 0; i < foundShapes.length; i++)\n            {\n                const shape = foundShapes[i];\n                const transform = shape.transform.worldTransform;\n\n                this.renderer.canvasContext.setContextTransform(transform);\n\n                this.renderGraphicsShape(shape);\n            }\n\n            foundShapes.length = 0;\n            context.clip();\n        }\n    }\n\n    /**\n     * Renders all PIXI.Graphics shapes in a subtree.\n     * @param container - container to scan.\n     * @param out - where to put found shapes\n     */\n    recursiveFindShapes(container: Container, out: Array<Graphics>): void\n    {\n        if ((container as Graphics).geometry && (container as Graphics).geometry.graphicsData)\n        {\n            out.push(container as Graphics);\n        }\n\n        const { children } = container;\n\n        if (children)\n        {\n            for (let i = 0; i < children.length; i++)\n            {\n                this.recursiveFindShapes(children[i] as Container, out);\n            }\n        }\n    }\n\n    /**\n     * Renders a PIXI.Graphics shape.\n     * @param graphics - The object to render.\n     */\n    renderGraphicsShape(graphics: Graphics): void\n    {\n        graphics.finishPoly();\n\n        const context = this.renderer.canvasContext.activeContext;\n        const graphicsData = graphics.geometry.graphicsData;\n        const len = graphicsData.length;\n\n        if (len === 0)\n        {\n            return;\n        }\n\n        for (let i = 0; i < len; i++)\n        {\n            const data = graphicsData[i];\n            const shape = data.shape;\n\n            if (shape.type === SHAPES.POLY)\n            {\n                let points = shape.points;\n                const holes = data.holes;\n                let outerArea;\n                let innerArea;\n                let px;\n                let py;\n\n                context.moveTo(points[0], points[1]);\n\n                for (let j = 1; j < points.length / 2; j++)\n                {\n                    context.lineTo(points[j * 2], points[(j * 2) + 1]);\n                }\n                if (holes.length > 0)\n                {\n                    outerArea = 0;\n                    px = points[0];\n                    py = points[1];\n                    for (let j = 2; j + 2 < points.length; j += 2)\n                    {\n                        outerArea += ((points[j] - px) * (points[j + 3] - py))\n                            - ((points[j + 2] - px) * (points[j + 1] - py));\n                    }\n\n                    for (let k = 0; k < holes.length; k++)\n                    {\n                        points = (holes[k].shape as Polygon).points;\n\n                        if (!points)\n                        {\n                            continue;\n                        }\n\n                        innerArea = 0;\n                        px = points[0];\n                        py = points[1];\n                        for (let j = 2; j + 2 < points.length; j += 2)\n                        {\n                            innerArea += ((points[j] - px) * (points[j + 3] - py))\n                                - ((points[j + 2] - px) * (points[j + 1] - py));\n                        }\n\n                        if (innerArea * outerArea < 0)\n                        {\n                            context.moveTo(points[0], points[1]);\n\n                            for (let j = 2; j < points.length; j += 2)\n                            {\n                                context.lineTo(points[j], points[j + 1]);\n                            }\n                        }\n                        else\n                        {\n                            context.moveTo(points[points.length - 2], points[points.length - 1]);\n\n                            for (let j = points.length - 4; j >= 0; j -= 2)\n                            {\n                                context.lineTo(points[j], points[j + 1]);\n                            }\n                        }\n\n                        if ((holes[k].shape as Polygon).closeStroke)\n                        {\n                            context.closePath();\n                        }\n                    }\n                }\n                // if the first and last point are the same close the path - much neater :)\n                if (points[0] === points[points.length - 2] && points[1] === points[points.length - 1])\n                {\n                    context.closePath();\n                }\n            }\n            else if (shape.type === SHAPES.RECT)\n            {\n                context.rect(shape.x, shape.y, shape.width, shape.height);\n                context.closePath();\n            }\n            else if (shape.type === SHAPES.CIRC)\n            {\n                // TODO - need to be Undefined!\n                context.arc(shape.x, shape.y, shape.radius, 0, 2 * Math.PI);\n                context.closePath();\n            }\n            else if (shape.type === SHAPES.ELIP)\n            {\n                // ellipse code taken from: http://stackoverflow.com/questions/2172798/how-to-draw-an-oval-in-html5-canvas\n\n                const w = shape.width * 2;\n                const h = shape.height * 2;\n\n                const x = shape.x - (w / 2);\n                const y = shape.y - (h / 2);\n\n                const kappa = 0.5522848;\n                const ox = (w / 2) * kappa; // control point offset horizontal\n                const oy = (h / 2) * kappa; // control point offset vertical\n                const xe = x + w; // x-end\n                const ye = y + h; // y-end\n                const xm = x + (w / 2); // x-middle\n                const ym = y + (h / 2); // y-middle\n\n                context.moveTo(x, ym);\n                context.bezierCurveTo(x, ym - oy, xm - ox, y, xm, y);\n                context.bezierCurveTo(xm + ox, y, xe, ym - oy, xe, ym);\n                context.bezierCurveTo(xe, ym + oy, xm + ox, ye, xm, ye);\n                context.bezierCurveTo(xm - ox, ye, x, ym + oy, x, ym);\n                context.closePath();\n            }\n            else if (shape.type === SHAPES.RREC)\n            {\n                const rx = shape.x;\n                const ry = shape.y;\n                const width = shape.width;\n                const height = shape.height;\n                let radius = shape.radius;\n\n                const maxRadius = Math.min(width, height) / 2;\n\n                radius = radius > maxRadius ? maxRadius : radius;\n\n                context.moveTo(rx, ry + radius);\n                context.lineTo(rx, ry + height - radius);\n                context.quadraticCurveTo(rx, ry + height, rx + radius, ry + height);\n                context.lineTo(rx + width - radius, ry + height);\n                context.quadraticCurveTo(rx + width, ry + height, rx + width, ry + height - radius);\n                context.lineTo(rx + width, ry + radius);\n                context.quadraticCurveTo(rx + width, ry, rx + width - radius, ry);\n                context.lineTo(rx + radius, ry);\n                context.quadraticCurveTo(rx, ry, rx, ry + radius);\n                context.closePath();\n            }\n        }\n    }\n\n    /**\n     * Restores the current drawing context to the state it was before the mask was applied.\n     * @param renderer - The renderer context to use.\n     */\n    popMask(renderer: CanvasRenderer): void\n    {\n        renderer.canvasContext.activeContext.restore();\n        renderer.canvasContext.invalidateBlendMode();\n    }\n\n    /** Destroys this canvas mask manager. */\n    public destroy(): void\n    {\n        /* empty */\n    }\n}\n\nextensions.add(CanvasMaskSystem);\n"],"names":[],"mappings":"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"}