import {
extensions,
ExtensionType,
RENDERER_TYPE,
settings,
SystemManager,
utils
} from '@pixi/core';
import type {
BackgroundSystem,
BLEND_MODES,
ColorSource,
ExtensionMetadata,
GenerateTextureSystem,
ICanvas,
ICanvasRenderingContext2D,
IGenerateTextureOptions,
IRenderableObject,
IRenderer,
IRendererOptions,
IRendererPlugins,
IRendererRenderOptions,
Matrix,
PluginSystem,
Rectangle,
RenderTexture,
StartupSystem,
ViewSystem,
} from '@pixi/core';
import type { DisplayObject } from '@pixi/display';
import type { CanvasContextSystem, SmoothingEnabledProperties } from './CanvasContextSystem';
import type { CanvasMaskSystem } from './CanvasMaskSystem';
import type { CanvasObjectRendererSystem } from './CanvasObjectRendererSystem';
const { deprecation } = utils;
// eslint-disable-next-line @typescript-eslint/no-empty-interface
export interface CanvasRenderer extends GlobalMixins.CanvasRenderer {}
/**
* The CanvasRenderer draws the scene and all its content onto a 2d canvas.
*
* This renderer should be used for browsers that support WebGL.
*
* This renderer should be used for browsers that do not support WebGL.
* Don't forget to add the view to your DOM or you will not see anything!
*
* Renderer is composed of systems that manage specific tasks. The following systems are added by default
* whenever you create a renderer:
*
* | System | Description |
* | ------------------------------------ | ----------------------------------------------------------------------------- |
*
* | Generic Systems | Systems that manage functionality that all renderer types share |
* | ------------------------------------ | ----------------------------------------------------------------------------- |
* | PIXI.ViewSystem | This manages the main view of the renderer usually a Canvas |
* | PIXI.PluginSystem | This manages plugins for the renderer |
* | PIXI.BackgroundSystem | This manages the main views background color and alpha |
* | PIXI.StartupSystem | Boots up a renderer and initiatives all the systems |
* | PIXI.EventSystem | This manages UI events. |
* | PIXI.GenerateTextureSystem | This adds the ability to generate textures from any PIXI.DisplayObject |
*
* | PixiJS High-Level Systems | Set of specific systems designed to work with PixiJS objects |
* | ------------------------------------ | ----------------------------------------------------------------------------- |
* | PIXI.CanvasContextSystem | This manages the canvas `2d` contexts and their state |
* | PIXI.CanvasMaskSystem | This manages masking operations. |
* | PIXI.CanvasExtract | This extracts image data from a PIXI.DisplayObject |
* | PIXI.CanvasPrepare | This prepares a PIXI.DisplayObject async for rendering |
*
* The breadth of the API surface provided by the renderer is contained within these systems.
* @class
* @memberof PIXI
*/
export class CanvasRenderer extends SystemManager<CanvasRenderer> implements IRenderer
{
/** @ignore */
static extension: ExtensionMetadata = {
type: ExtensionType.Renderer,
priority: 0,
};
/**
* Options passed to the constructor.
* @member {PIXI.IRendererOptions}
*/
public readonly options: IRendererOptions;
/**
* Used with autoDetectRenderer, this is always supported for any environment, so return true.
* @ignore
*/
static test(): boolean
{
return true;
}
/**
* Fired after rendering finishes.
* @event PIXI.CanvasRenderer#postrender
*/
/**
* Fired before rendering starts.
* @event PIXI.CanvasRenderer#prerender
*/
/**
* The type of the renderer. will be PIXI.RENDERER_TYPE.CANVAS
* @member {number}
* @see PIXI.RENDERER_TYPE
*/
public readonly type = RENDERER_TYPE.CANVAS;
/** When logging Pixi to the console, this is the name we will show */
public readonly rendererLogId = 'Canvas';
// systems..
/**
* textureGenerator system instance
* @readonly
*/
public textureGenerator: GenerateTextureSystem;
/**
* background system instance
* @readonly
*/
public background: BackgroundSystem;
/**
* canvas mask system instance
* @readonly
*/
public mask: CanvasMaskSystem;
/**
* plugin system instance
* @readonly
*/
public _plugin: PluginSystem;
/**
* Canvas context system instance
* @readonly
*/
public canvasContext: CanvasContextSystem;
/**
* Startup system instance
* @readonly
*/
public startup: StartupSystem;
/**
* View system instance
* @readonly
*/
public _view: ViewSystem;
/**
* renderer system instance
* @readonly
*/
public objectRenderer: CanvasObjectRendererSystem;
/**
* @param {PIXI.IRendererOptions} [options] - See PIXI.settings.RENDER_OPTIONS for defaults.
*/
constructor(options?: Partial<IRendererOptions>)
{
super();
// Add the default render options
options = Object.assign({}, settings.RENDER_OPTIONS, options);
const systemConfig = {
runners: [
'init',
'destroy',
'contextChange',
'resolutionChange',
'reset',
'update',
'postrender',
'prerender',
'resize'
],
systems: CanvasRenderer.__systems,
priority: [
'textureGenerator',
'background',
'_view',
'_plugin',
'startup',
'mask',
'canvasContext',
'objectRenderer'
],
};
this.setup(systemConfig);
if ('useContextAlpha' in options)
{
if (process.env.DEBUG)
{
deprecation('7.0.0', 'options.useContextAlpha is deprecated, use options.backgroundAlpha instead');
}
options.backgroundAlpha = options.useContextAlpha === false ? 1 : options.backgroundAlpha;
}
// convert our big blob of options into system specific ones..
this._plugin.rendererPlugins = CanvasRenderer.__plugins;
this.options = options as IRendererOptions;
this.startup.run(this.options);
}
/**
* Useful function that returns a texture of the display object that can then be used to create sprites
* This can be quite useful if your displayObject is complicated and needs to be reused multiple times.
* @param displayObject - The displayObject the object will be generated from.
* @param {IGenerateTextureOptions} options - Generate texture options.
* @param {PIXI.Rectangle} options.region - The region of the displayObject, that shall be rendered,
* if no region is specified, defaults to the local bounds of the displayObject.
* @param {number} [options.resolution] - If not given, the renderer's resolution is used.
* @param {PIXI.MSAA_QUALITY} [options.multisample] - If not given, the renderer's multisample is used.
* @returns A texture of the graphics object.
*/
generateTexture(displayObject: IRenderableObject, options?: IGenerateTextureOptions): RenderTexture
{
return this.textureGenerator.generateTexture(displayObject, options);
}
reset(): void
{
// nothing to be done :D
}
/**
* Renders the object to its WebGL view.
* @param displayObject - The object to be rendered.
* @param options - Object to use for render options.
* @param {PIXI.RenderTexture} [options.renderTexture] - The render texture to render to.
* @param {boolean} [options.clear=true] - Should the canvas be cleared before the new render.
* @param {PIXI.Matrix} [options.transform] - A transform to apply to the render texture before rendering.
* @param {boolean} [options.skipUpdateTransform=false] - Should we skip the update transform pass?
*/
render(displayObject: DisplayObject, options?: IRendererRenderOptions): void
{
this.objectRenderer.render(displayObject, options);
}
/** Clear the canvas of renderer. */
public clear(): void
{
this.canvasContext.clear();
}
/**
* Removes everything from the renderer and optionally removes the Canvas DOM element.
* @param {boolean} [removeView=false] - Removes the Canvas element from the DOM.
*/
public destroy(removeView?: boolean): void
{
this.runners.destroy.items.reverse();
this.emitWithCustomOptions(this.runners.destroy, {
_view: removeView,
});
super.destroy();
}
/** Collection of plugins */
get plugins(): IRendererPlugins
{
return this._plugin.plugins;
}
/**
* Resizes the canvas view to the specified width and height.
* @param desiredScreenWidth - the desired width of the screen
* @param desiredScreenHeight - the desired height of the screen
*/
public resize(desiredScreenWidth: number, desiredScreenHeight: number): void
{
this._view.resizeView(desiredScreenWidth, desiredScreenHeight);
}
/**
* Same as view.width, actual number of pixels in the canvas by horizontal.
* @member {number}
* @readonly
* @default 800
*/
get width(): number
{
return this._view.element.width;
}
/**
* Same as view.height, actual number of pixels in the canvas by vertical.
* @member {number}
* @readonly
* @default 600
*/
get height(): number
{
return this._view.element.height;
}
/** The resolution / device pixel ratio of the renderer. */
get resolution(): number
{
return this._view.resolution;
}
set resolution(value: number)
{
this._view.resolution = value;
this.runners.resolutionChange.emit(value);
}
/** Whether CSS dimensions of canvas view should be resized to screen dimensions automatically. */
get autoDensity(): boolean
{
return this._view.autoDensity;
}
/** The canvas element that everything is drawn to.*/
get view(): ICanvas
{
return this._view.element;
}
/**
* Measurements of the screen. (0, 0, screenWidth, screenHeight).
* Its safe to use as filterArea or hitArea for the whole stage.
*/
get screen(): Rectangle
{
return this._view.screen;
}
/** the last object rendered by the renderer. Useful for other plugins like interaction managers */
get lastObjectRendered(): IRenderableObject
{
return this.objectRenderer.lastObjectRendered;
}
/** Flag if we are rendering to the screen vs renderTexture */
get renderingToScreen(): boolean
{
return this.objectRenderer.renderingToScreen;
}
/**
* This sets if the CanvasRenderer will clear the canvas or not before the new render pass.
* If the scene is NOT transparent PixiJS will use a canvas sized fillRect operation every
* frame to set the canvas background color. If the scene is transparent PixiJS will use clearRect
* to clear the canvas every frame. Disable this by setting this to false. For example, if
* your game has a canvas filling background image you often don't need this set.
*/
get clearBeforeRender(): boolean
{
return this.background.clearBeforeRender;
}
// deprecated zone..
/**
* Tracks the blend modes useful for this renderer.
* @deprecated since 7.0.0 use `renderer.canvasContext.blendModes` instead
*/
get blendModes(): string[]
{
if (process.env.DEBUG)
{
// eslint-disable-next-line max-len
deprecation('7.0.0', 'renderer.blendModes has been deprecated, please use renderer.canvasContext.blendModes instead');
}
return this.canvasContext.blendModes;
}
/**
* system that manages canvas masks
* @deprecated since 7.0.0 use `renderer.canvasContext.mask`
*/
get maskManager(): CanvasMaskSystem
{
deprecation('7.0.0', 'renderer.maskManager has been deprecated, please use renderer.mask instead');
return this.mask;
}
/**
* Boolean flag controlling canvas refresh.
* @deprecated since 7.0.0
*/
get refresh(): boolean
{
if (process.env.DEBUG)
{
deprecation('7.0.0', 'renderer.refresh has been deprecated');
}
return true;
}
/**
* The root canvas 2d context that everything is drawn with.
* @deprecated since 7.0.0 Use `renderer.canvasContext.rootContext instead
*/
get rootContext(): ICanvasRenderingContext2D
{
if (process.env.DEBUG)
{
// eslint-disable-next-line max-len
deprecation('7.0.0', 'renderer.rootContext has been deprecated, please use renderer.canvasContext.rootContext instead');
}
return this.canvasContext.rootContext;
}
/**
* The currently active canvas 2d context (could change with renderTextures)
* @deprecated since 7.0.0 Use `renderer.canvasContext.activeContext instead
*/
get context(): ICanvasRenderingContext2D
{
if (process.env.DEBUG)
{
// eslint-disable-next-line max-len
deprecation('7.0.0', 'renderer.context has been deprecated, please use renderer.canvasContext.activeContext instead');
}
return this.canvasContext.activeContext;
}
/**
* The canvas property used to set the canvas smoothing property.
* @deprecated since 7.0.0 Use `renderer.canvasContext.smoothProperty` instead.
*/
get smoothProperty(): SmoothingEnabledProperties
{
if (process.env.DEBUG)
{
// eslint-disable-next-line max-len
deprecation('7.0.0', 'renderer.smoothProperty has been deprecated, please use renderer.canvasContext.smoothProperty instead');
}
return this.canvasContext.smoothProperty;
}
/**
* Sets the blend mode of the renderer.
* @param {number} blendMode - See PIXI.BLEND_MODES for valid values.
* @param {boolean} [readyForOuterBlend=false] - Some blendModes are dangerous, they affect outer space of sprite.
* Pass `true` only if you are ready to use them.
* @deprecated since 7.0.0 Use `renderer.canvasContext.setBlendMode` instead.
*/
setBlendMode(blendMode: BLEND_MODES, readyForOuterBlend?: boolean): void
{
if (process.env.DEBUG)
{
// eslint-disable-next-line max-len
deprecation('7.0.0', 'renderer.setBlendMode has been deprecated, use renderer.canvasContext.setBlendMode instead');
}
this.canvasContext.setBlendMode(blendMode, readyForOuterBlend);
}
/**
* Checks if blend mode has changed.
* @deprecated since 7.0.0 Use `renderer.canvasContext.invalidateBlendMode` instead.
*/
invalidateBlendMode(): void
{
if (process.env.DEBUG)
{
// eslint-disable-next-line max-len
deprecation('7.0.0', 'renderer.invalidateBlendMode has been deprecated, use renderer.canvasContext.invalidateBlendMode instead');
}
this.canvasContext.invalidateBlendMode();
}
/**
* Sets matrix of context.
* called only from render() methods
* takes care about resolution
* @param transform - world matrix of current element
* @param roundPixels - whether to round (tx,ty) coords
* @param localResolution - If specified, used instead of `renderer.resolution` for local scaling
* @deprecated since 7.0.0 - Use `renderer.canvasContext.setContextTransform` instead.
*/
setContextTransform(transform: Matrix, roundPixels?: boolean, localResolution?: number): void
{
if (process.env.DEBUG)
{
// eslint-disable-next-line max-len
deprecation('7.0.0', 'renderer.setContextTransform has been deprecated, use renderer.canvasContext.setContextTransform instead');
}
this.canvasContext.setContextTransform(transform, roundPixels, localResolution);
}
/**
* The background color to fill if not transparent
* @deprecated since 7.0.0
*/
get backgroundColor(): ColorSource
{
if (process.env.DEBUG)
{
// eslint-disable-next-line max-len
deprecation('7.0.0', 'renderer.backgroundColor has been deprecated, use renderer.background.color instead.');
}
return this.background.color;
}
/**
* @deprecated since 7.0.0
* @ignore
*/
set backgroundColor(value: ColorSource)
{
if (process.env.DEBUG)
{
deprecation('7.0.0', 'renderer.backgroundColor has been deprecated, use renderer.background.color instead.');
}
this.background.color = value;
}
/**
* The background color alpha. Setting this to 0 will make the canvas transparent.
* @member {number}
* @deprecated since 7.0.0
*/
get backgroundAlpha(): number
{
if (process.env.DEBUG)
{
// eslint-disable-next-line max-len
deprecation('7.0.0', 'renderer.backgroundAlpha has been deprecated, use renderer.background.alpha instead.');
}
return this.background.alpha;
}
/**
* @deprecated since 7.0.0
* @ignore
*/
set backgroundAlpha(value: number)
{
if (process.env.DEBUG)
{
// eslint-disable-next-line max-len
deprecation('7.0.0', 'renderer.backgroundAlpha has been deprecated, use renderer.background.alpha instead.');
}
this.background.alpha = value;
}
/**
* old abstract function not used by canvas renderer
* @deprecated since 7.0.0
*/
get preserveDrawingBuffer(): boolean
{
if (process.env.DEBUG)
{
deprecation('7.0.0', 'renderer.preserveDrawingBuffer has been deprecated');
}
return false;
}
/**
* old abstract function not used by canvas renderer
* @deprecated since 7.0.0
*/
get useContextAlpha(): boolean
{
if (process.env.DEBUG)
{
deprecation('7.0.0', 'renderer.useContextAlpha has been deprecated');
}
return false;
}
/** @private */
static readonly __plugins: IRendererPlugins = {};
/** @private */
static readonly __systems: Record<string, any> = {};
/**
* Collection of installed plugins. These are included by default in PIXI, but can be excluded
* by creating a custom build. Consult the README for more information about creating custom
* builds and excluding plugins.
* @member {object} plugins
* @readonly
* @property {PIXI.AccessibilityManager} accessibility Support tabbing interactive elements.
*/
}
extensions.handleByMap(ExtensionType.CanvasRendererPlugin, CanvasRenderer.__plugins);
extensions.handleByMap(ExtensionType.CanvasRendererSystem, CanvasRenderer.__systems);
extensions.add(CanvasRenderer);