import { GlProgram } from '../rendering/renderers/gl/shader/GlProgram';
import { GpuProgram } from '../rendering/renderers/gpu/shader/GpuProgram';
import { Shader } from '../rendering/renderers/shared/shader/Shader';
import { State } from '../rendering/renderers/shared/state/State';
import type { RenderSurface } from '../rendering/renderers/shared/renderTarget/RenderTargetSystem';
import type {
IShaderWithResources,
ShaderFromResources,
ShaderWithResources
} from '../rendering/renderers/shared/shader/Shader';
import type { BLEND_MODES } from '../rendering/renderers/shared/state/const';
import type { Texture } from '../rendering/renderers/shared/texture/Texture';
import type { FilterSystem } from './FilterSystem';
/**
* Filters provide additional shading and post-processing effects to any display object and its children
* they are attached to.
*
* You attached filters to a display object using its `filters` array property.
*
* ```js
* import { Sprite, BlurFilter, HardMixBlend } from 'pixi.js';
*
* const sprite = Sprite.from('myTexture.png');
*
* // single filter
* sprite.filters = new BlurFilter({ strength: 8 });
*
* // or multiple filters
* sprite.filters = [new BlurFilter({ strength: 8 }), new HardMixBlend()];
* ```
*
* Pixi has a number of built-in filters which can be used in your game or application:
*
* - AlphaFilter - Applies alpha to the display object and any of its children.
* - BlurFilter - Applies a Gaussian blur to the display object.
* - BlurFilterPass - Applies a blur pass to an object.
* - ColorBurnBlend - Blend mode to add color burn to display objects.
* - ColorDodgeBlend - Blend mode to add color dodge to display objects.
* - ColorMatrixFilter - Transform the color channels by matrix multiplication.
* - DarkenBlend - Blend mode to darken display objects.
* - DisplacementFilter - Applies a displacement map to distort an object.
* - DivideBlend - Blend mode to divide display objects.
* - HardMixBlend - Blend mode to hard mix display objects.
* - LinearBurnBlend - Blend mode to add linear burn to display objects.
* - LinearDodgeBlend - Blend mode to add linear dodge to display objects.
* - LinearLightBlend - Blend mode to add linear light to display objects.
* - NoiseFilter - Applies random noise to an object.
* - PinLightBlend - Blend mode to add pin light to display objects.
* - SubtractBlend - Blend mode to subtract display objects.
*
* <br/>
* For more available filters, check out the
* pixi-filters repository.
*
* You can also check out the awesome Filter demo to see
* filters in action and combine them!
* @namespace filters
*/
/**
* The options to use when creating a new filter.
* @memberof filters
*/
export interface FilterOptions
{
/** optional blend mode used by the filter when rendering (defaults to 'normal') */
blendMode?: BLEND_MODES;
/**
* the resolution the filter should be rendered at. The lower the resolution, the more performant
* the filter will be, but the lower the quality of the output. (defaults to the renderers resolution)
* Consider lowering this for things like blurs filters
*/
resolution?: number;
/**
* the amount of pixels to pad the container with when applying the filter. For example a blur extends the
* container out as it blurs, so padding is applied to ensure that extra detail is rendered as well
* without clipping occurring. (default 0)
*/
padding?: number;
/**
* If true the filter will make use of antialiasing. Although it looks better this can have a performance impact.
* By default, the filter will detect the antialiasing of the renderer and change this automatically.
* Definitely don't set this to true if the renderer has antialiasing set to false. As it will antialias,
* but you won't see the difference.
*
* This can be a boolean or FilterAntialias string.
*/
antialias?: FilterAntialias | boolean;
/**
* If this is set to true, the filter system will grab a snap shot oif the are being rendered
* to and pass this into the shader. This is useful for blend modes that need to be aware of the pixels
* they are rendering to. Only use if you need that data, otherwise its an extra gpu copy you don't need!
* (default false)
*/
blendRequired?: boolean;
}
/** Filter options mixed with shader resources. A filter needs a shader and some resources to work. */
export type FilterWithShader = FilterOptions & IShaderWithResources;
/**
* The antialiasing mode of the filter. This can be either:
* - `on` - the filter is always antialiased regardless of the renderer settings
* - `off` - the filter is never antialiased regardless of the renderer settings
* - `inherit` - (default) the filter uses the antialias settings of the renderer
* @memberof filters
*/
export type FilterAntialias = 'on' | 'off' | 'inherit';
/**
* The Filter class is the base for all filter effects used in Pixi.js
* As it extends a shader, it requires that a glProgram is parsed in to work with WebGL and a gpuProgram for WebGPU.
* If you don't proved one, then the filter is skipped and just rendered as if it wasn't there for that renderer.
*
* A filter can be applied to anything that extends Container in Pixi.js which also includes Sprites, Graphics etc.
*
* Its worth noting Performance-wise filters can be pretty expensive if used too much in a single scene.
* The following happens under the hood when a filter is applied:
*
* .1. Break the current batch
* <br>
* .2. The target is measured using getGlobalBounds
* (recursively go through all children and figure out how big the object is)
* <br>
* .3. Get the closest Po2 Textures from the texture pool
* <br>
* .4. Render the target to that texture
* <br>
* .5. Render that texture back to the main frame buffer as a quad using the filters program.
* <br>
* <br>
* Some filters (such as blur) require multiple passes too which can result in an even bigger performance hit. So be careful!
* Its not generally the complexity of the shader that is the bottle neck,
* but all the framebuffer / shader switching that has to take place.
* One filter applied to a container with many objects is MUCH faster than many filter applied to many objects.
* @class
* @memberof filters
*/
export class Filter extends Shader
{
/**
* The default filter settings
* @static
*/
public static readonly defaultOptions: FilterOptions = {
blendMode: 'normal',
resolution: 1,
padding: 0,
antialias: 'off',
blendRequired: false,
};
/**
* The padding of the filter. Some filters require extra space to breath such as a blur.
* Increasing this will add extra width and height to the bounds of the object that the
* filter is applied to.
* @default 0
*/
public padding: number;
/**
* should the filter use antialiasing?
* @default inherit
*/
public antialias: FilterAntialias;
/** If enabled is true the filter is applied, if false it will not. */
public enabled = true;
/**
* The gpu state the filter requires to render.
* @internal
* @ignore
*/
public _state = State.for2d();
/**
* The resolution of the filter. Setting this to be lower will lower the quality but
* increase the performance of the filter.
* @default 1
*/
public resolution: number;
/**
* Whether or not this filter requires the previous render texture for blending.
* @default false
*/
public blendRequired: boolean;
/**
* @param options - The optional parameters of this filter.
*/
constructor(options: FilterWithShader)
{
options = { ...Filter.defaultOptions, ...options };
super(options as ShaderWithResources);
this.padding = options.padding;
// check if is boolean
if (typeof options.antialias === 'boolean')
{
this.antialias = options.antialias ? 'on' : 'off';
}
else
{
this.antialias = options.antialias;
}
this.resolution = options.resolution;
this.blendRequired = options.blendRequired;
this.addResource('uTexture', 0, 1);
}
/**
* Applies the filter
* @param filterManager - The renderer to retrieve the filter from
* @param input - The input render target.
* @param output - The target to output to.
* @param clearMode - Should the output be cleared before rendering to it
*/
public apply(
filterManager: FilterSystem,
input: Texture,
output: RenderSurface,
clearMode: boolean
): void
{
filterManager.applyFilter(this, input, output, clearMode);
}
/**
* Get the blend mode of the filter.
* @default "normal"
*/
get blendMode(): BLEND_MODES
{
return this._state.blendMode;
}
/** Sets the blend mode of the filter. */
set blendMode(value: BLEND_MODES)
{
this._state.blendMode = value;
}
/**
* A short hand function to create a filter based of a vertex and fragment shader src.
* @param options
* @returns A shiny new PixiJS filter!
*/
public static from(options: FilterOptions & ShaderFromResources): Filter
{
const { gpu, gl, ...rest } = options;
let gpuProgram: GpuProgram;
let glProgram: GlProgram;
if (gpu)
{
gpuProgram = GpuProgram.from(gpu);
}
if (gl)
{
glProgram = GlProgram.from(gl);
}
return new Filter({
gpuProgram,
glProgram,
...rest
});
}
}