UNPKG

14.2 kBSource Map (JSON)View Raw
1{"version":3,"file":"MaskSystem.mjs","sources":["../../src/mask/MaskSystem.ts"],"sourcesContent":["import { MASK_TYPES } from '@pixi/constants';\nimport { extensions, ExtensionType } from '@pixi/extensions';\nimport { SpriteMaskFilter } from '../filters/spriteMask/SpriteMaskFilter';\nimport { MaskData } from './MaskData';\n\nimport type { ExtensionMetadata } from '@pixi/extensions';\nimport type { Renderer } from '../Renderer';\nimport type { ISystem } from '../system/ISystem';\nimport type { IMaskTarget } from './MaskData';\n\n/**\n * System plugin to the renderer to manage masks.\n *\n * There are three built-in types of masking:\n * **Scissor Masking**: Scissor masking discards pixels that are outside of a rectangle called the scissor box. It is\n * the most performant as the scissor test is inexpensive. However, it can only be used when the mask is rectangular.\n * **Stencil Masking**: Stencil masking discards pixels that don't overlap with the pixels rendered into the stencil\n * buffer. It is the next fastest option as it does not require rendering into a separate framebuffer. However, it does\n * cause the mask to be rendered **twice** for each masking operation; hence, minimize the rendering cost of your masks.\n * **Sprite Mask Filtering**: Sprite mask filtering discards pixels based on the red channel of the sprite-mask's\n * texture. (Generally, the masking texture is grayscale). Using advanced techniques, you might be able to embed this\n * type of masking in a custom shader - and hence, bypassing the masking system fully for performance wins.\n *\n * The best type of masking is auto-detected when you `push` one. To use scissor masking, you must pass in a `Graphics`\n * object with just a rectangle drawn.\n *\n * ## Mask Stacks\n *\n * In the scene graph, masks can be applied recursively, i.e. a mask can be applied during a masking operation. The mask\n * stack stores the currently applied masks in order. Each {@link PIXI.BaseRenderTexture} holds its own mask stack, i.e.\n * when you switch render-textures, the old masks only applied when you switch back to rendering to the old render-target.\n * @memberof PIXI\n */\nexport class MaskSystem implements ISystem\n{\n /** @ignore */\n static extension: ExtensionMetadata = {\n type: ExtensionType.RendererSystem,\n name: 'mask',\n };\n\n /**\n * Flag to enable scissor masking.\n * @default true\n */\n public enableScissor: boolean;\n\n /** Pool of used sprite mask filters. */\n protected readonly alphaMaskPool: Array<SpriteMaskFilter[]>;\n\n /**\n * Current index of alpha mask pool.\n * @default 0\n * @readonly\n */\n protected alphaMaskIndex: number;\n\n /** Pool of mask data. */\n private readonly maskDataPool: Array<MaskData>;\n private maskStack: Array<MaskData>;\n private renderer: Renderer;\n\n /**\n * @param renderer - The renderer this System works for.\n */\n constructor(renderer: Renderer)\n {\n this.renderer = renderer;\n\n this.enableScissor = true;\n this.alphaMaskPool = [];\n this.maskDataPool = [];\n\n this.maskStack = [];\n this.alphaMaskIndex = 0;\n }\n\n /**\n * Changes the mask stack that is used by this System.\n * @param maskStack - The mask stack\n */\n setMaskStack(maskStack: Array<MaskData>): void\n {\n this.maskStack = maskStack;\n this.renderer.scissor.setMaskStack(maskStack);\n this.renderer.stencil.setMaskStack(maskStack);\n }\n\n /**\n * Enables the mask and appends it to the current mask stack.\n *\n * NOTE: The batch renderer should be flushed beforehand to prevent pending renders from being masked.\n * @param {PIXI.DisplayObject} target - Display Object to push the mask to\n * @param {PIXI.MaskData|PIXI.Sprite|PIXI.Graphics|PIXI.DisplayObject} maskDataOrTarget - The masking data.\n */\n push(target: IMaskTarget, maskDataOrTarget: MaskData | IMaskTarget): void\n {\n let maskData = maskDataOrTarget as MaskData;\n\n if (!maskData.isMaskData)\n {\n const d = this.maskDataPool.pop() || new MaskData();\n\n d.pooled = true;\n d.maskObject = maskDataOrTarget as IMaskTarget;\n maskData = d;\n }\n\n const maskAbove = this.maskStack.length !== 0 ? this.maskStack[this.maskStack.length - 1] : null;\n\n maskData.copyCountersOrReset(maskAbove);\n maskData._colorMask = maskAbove ? maskAbove._colorMask : 0xf;\n\n if (maskData.autoDetect)\n {\n this.detect(maskData);\n }\n\n maskData._target = target;\n\n if (maskData.type !== MASK_TYPES.SPRITE)\n {\n this.maskStack.push(maskData);\n }\n\n if (maskData.enabled)\n {\n switch (maskData.type)\n {\n case MASK_TYPES.SCISSOR:\n this.renderer.scissor.push(maskData);\n break;\n case MASK_TYPES.STENCIL:\n this.renderer.stencil.push(maskData);\n break;\n case MASK_TYPES.SPRITE:\n maskData.copyCountersOrReset(null);\n this.pushSpriteMask(maskData);\n break;\n case MASK_TYPES.COLOR:\n this.pushColorMask(maskData);\n break;\n default:\n break;\n }\n }\n\n if (maskData.type === MASK_TYPES.SPRITE)\n {\n this.maskStack.push(maskData);\n }\n }\n\n /**\n * Removes the last mask from the mask stack and doesn't return it.\n *\n * NOTE: The batch renderer should be flushed beforehand to render the masked contents before the mask is removed.\n * @param {PIXI.IMaskTarget} target - Display Object to pop the mask from\n */\n pop(target: IMaskTarget): void\n {\n const maskData = this.maskStack.pop();\n\n if (!maskData || maskData._target !== target)\n {\n // TODO: add an assert when we have it\n\n return;\n }\n\n if (maskData.enabled)\n {\n switch (maskData.type)\n {\n case MASK_TYPES.SCISSOR:\n this.renderer.scissor.pop(maskData);\n break;\n case MASK_TYPES.STENCIL:\n this.renderer.stencil.pop(maskData.maskObject);\n break;\n case MASK_TYPES.SPRITE:\n this.popSpriteMask(maskData);\n break;\n case MASK_TYPES.COLOR:\n this.popColorMask(maskData);\n break;\n default:\n break;\n }\n }\n\n maskData.reset();\n\n if (maskData.pooled)\n {\n this.maskDataPool.push(maskData);\n }\n\n if (this.maskStack.length !== 0)\n {\n const maskCurrent = this.maskStack[this.maskStack.length - 1];\n\n if (maskCurrent.type === MASK_TYPES.SPRITE && maskCurrent._filters)\n {\n maskCurrent._filters[0].maskSprite = maskCurrent.maskObject;\n }\n }\n }\n\n /**\n * Sets type of MaskData based on its maskObject.\n * @param maskData\n */\n detect(maskData: MaskData): void\n {\n const maskObject = maskData.maskObject;\n\n if (!maskObject)\n {\n maskData.type = MASK_TYPES.COLOR;\n }\n else if (maskObject.isSprite)\n {\n maskData.type = MASK_TYPES.SPRITE;\n }\n else if (this.enableScissor && this.renderer.scissor.testScissor(maskData))\n {\n maskData.type = MASK_TYPES.SCISSOR;\n }\n else\n {\n maskData.type = MASK_TYPES.STENCIL;\n }\n }\n\n /**\n * Applies the Mask and adds it to the current filter stack.\n * @param maskData - Sprite to be used as the mask.\n */\n pushSpriteMask(maskData: MaskData): void\n {\n const { maskObject } = maskData;\n const target = maskData._target;\n let alphaMaskFilter = maskData._filters;\n\n if (!alphaMaskFilter)\n {\n alphaMaskFilter = this.alphaMaskPool[this.alphaMaskIndex];\n\n if (!alphaMaskFilter)\n {\n alphaMaskFilter = this.alphaMaskPool[this.alphaMaskIndex] = [new SpriteMaskFilter()];\n }\n }\n\n alphaMaskFilter[0].resolution = maskData.resolution;\n alphaMaskFilter[0].multisample = maskData.multisample;\n alphaMaskFilter[0].maskSprite = maskObject;\n\n const stashFilterArea = target.filterArea;\n\n target.filterArea = maskObject.getBounds(true);\n this.renderer.filter.push(target, alphaMaskFilter);\n target.filterArea = stashFilterArea;\n\n if (!maskData._filters)\n {\n this.alphaMaskIndex++;\n }\n }\n\n /**\n * Removes the last filter from the filter stack and doesn't return it.\n * @param maskData - Sprite to be used as the mask.\n */\n popSpriteMask(maskData: MaskData): void\n {\n this.renderer.filter.pop();\n\n if (maskData._filters)\n {\n maskData._filters[0].maskSprite = null;\n }\n else\n {\n this.alphaMaskIndex--;\n this.alphaMaskPool[this.alphaMaskIndex][0].maskSprite = null;\n }\n }\n\n /**\n * Pushes the color mask.\n * @param maskData - The mask data\n */\n pushColorMask(maskData: MaskData): void\n {\n const currColorMask = maskData._colorMask;\n const nextColorMask = maskData._colorMask = currColorMask & maskData.colorMask;\n\n if (nextColorMask !== currColorMask)\n {\n this.renderer.gl.colorMask(\n (nextColorMask & 0x1) !== 0,\n (nextColorMask & 0x2) !== 0,\n (nextColorMask & 0x4) !== 0,\n (nextColorMask & 0x8) !== 0\n );\n }\n }\n\n /**\n * Pops the color mask.\n * @param maskData - The mask data\n */\n popColorMask(maskData: MaskData): void\n {\n const currColorMask = maskData._colorMask;\n const nextColorMask = this.maskStack.length > 0\n ? this.maskStack[this.maskStack.length - 1]._colorMask : 0xf;\n\n if (nextColorMask !== currColorMask)\n {\n this.renderer.gl.colorMask(\n (nextColorMask & 0x1) !== 0,\n (nextColorMask & 0x2) !== 0,\n (nextColorMask & 0x4) !== 0,\n (nextColorMask & 0x8) !== 0\n );\n }\n }\n\n destroy(): void\n {\n this.renderer = null;\n }\n}\n\nextensions.add(MaskSystem);\n"],"names":[],"mappings":"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"}
\No newline at end of file