UNPKG

phaser

Version:

A fast, free and fun HTML5 Game Framework for Desktop and Mobile web browsers from the team at Phaser Studio Inc.

192 lines (167 loc) 7.6 kB
/** * @author Benjamin D. Richards <benjamindrichards@gmail.com> * @copyright 2013-2026 Phaser Studio Inc. * @license {@link https://opensource.org/licenses/MIT|MIT License} */ var Rectangle = require('../../../../geom/rectangle/Rectangle'); var Class = require('../../../../utils/Class'); var BlendModes = require('../../../BlendModes'); var BaseFilter = require('./BaseFilter'); /** * @classdesc * A RenderNode that processes two independent filter chains — a bottom layer * and a top layer — each applied to the same input, then composites their * results together using a configurable blend mode. This allows you to combine * two completely different filter effects on a single Game Object; for example, * applying a glow to the bottom layer and a color correction to the top layer, * then merging them with an add or multiply blend. * * Each layer (bottom and top) runs its own ordered chain of filters in series, * where the output of one filter feeds into the next. Once both chains have * completed, the results are blended using the `FilterBlend` RenderNode * according to the controller's blend settings. * * If neither layer contains any active filters, the input is copied directly * to the output with no modifications. * * This node delegates rendering work to other RenderNodes during execution. * See {@link Phaser.Filters.ParallelFilters} for the associated controller. * * @class FilterParallelFilters * @extends Phaser.Renderer.WebGL.RenderNodes.BaseFilter * @memberof Phaser.Renderer.WebGL.RenderNodes * @constructor * @since 4.0.0 * @param {Phaser.Renderer.WebGL.RenderNodes.RenderNodeManager} manager - The manager that owns this RenderNode. */ var FilterParallelFilters = new Class({ Extends: BaseFilter, initialize: function FilterParallelFilters (manager) { BaseFilter.call(this, 'FilterParallelFilters', manager); }, /** * Runs the parallel filter process. The input drawing context is passed * through the bottom filter chain and the top filter chain independently, * producing two separately filtered results. Those results are then blended * together using the `FilterBlend` RenderNode with the blend mode and * settings defined on the controller. * * Padding is applied on the first filter in each chain only; subsequent * filters in the same chain receive zero padding to avoid double-expanding * the output bounds. * * If no filters are active on either layer, the input is copied directly * to the output unchanged. * * @method Phaser.Renderer.WebGL.RenderNodes.FilterParallelFilters#run * @since 4.0.0 * @param {Phaser.Filters.ParallelFilters} controller - The controller that defines the bottom and top filter chains and their blend settings. * @param {Phaser.Renderer.WebGL.DrawingContext} inputDrawingContext - The drawing context to use as the source image for both filter chains. * @param {Phaser.Renderer.WebGL.DrawingContext} outputDrawingContext - The drawing context to render the final blended result into. May be `null`, in which case a new context is allocated. * @param {Phaser.Geom.Rectangle} padding - The padding to apply around the rendered area on the first filter pass. * @return {Phaser.Renderer.WebGL.DrawingContext} The drawing context containing the final composited output. */ run: function (controller, inputDrawingContext, outputDrawingContext, padding) { this.onRunBegin(outputDrawingContext); // Prevent the input from being sent back to its pool. inputDrawingContext.lock(this); var bottomFilters = controller.bottom.getActive(); var topFilters = controller.top.getActive(); var initialPadding = padding || controller.getPaddingCeil(); if (bottomFilters.length + topFilters.length > 0) { var bottomContext = inputDrawingContext; var topContext = inputDrawingContext; // Process bottom filters. if (bottomFilters.length > 0) { padding = initialPadding; for (var i = 0; i < bottomFilters.length; i++) { var childController = bottomFilters[i]; var filter = this.manager.getNode(childController.renderNode); bottomContext = filter.run( childController, bottomContext, null, padding ); // Don't apply more padding after the first filter. if (i === 0 && i < bottomFilters.length - 1) { padding = new Rectangle(); } } } // Process top filters. if (topFilters.length > 0) { padding = initialPadding; for (i = 0; i < topFilters.length; i++) { childController = topFilters[i]; filter = this.manager.getNode(childController.renderNode); topContext = filter.run( childController, topContext, null, padding ); // Don't apply more padding after the first filter. if (i === 0 && i < topFilters.length - 1) { padding = new Rectangle(); } } } inputDrawingContext.unlock(this); // Check whether the input is no longer in use, // and won't be released automatically below. // If it is the bottom context, it will be released by the Blend. // If it is the top context, it will be released directly. if ( inputDrawingContext !== bottomContext && inputDrawingContext !== topContext ) { inputDrawingContext.release(); } // Blend the top and bottom filters. var blendController = controller.blend; blendController.glTexture = topContext.texture; filter = this.manager.getNode('FilterBlend'); outputDrawingContext = this.manager.getNode('FilterBlend').run( controller.blend, bottomContext, outputDrawingContext, padding // This will be 0 because at least one filter has already been applied. ); // Whether top context is new or the input, it now needs to be released. topContext.release(); } else { // No filters to run. // Copy the input to the output. filter = this.manager.getNode('FilterBlend'); var proxyController = { blendMode: BlendModes.COPY, glTexture: inputDrawingContext.texture, amount: 1, color: [ 1, 1, 1, 1 ] }; inputDrawingContext.unlock(this); outputDrawingContext = filter.run( proxyController, inputDrawingContext, outputDrawingContext, initialPadding ); } this.onRunEnd(outputDrawingContext); return outputDrawingContext; } }); module.exports = FilterParallelFilters;