import type { CoreNode } from '../../CoreNode.js'; import { normalizeCanvasColor } from '../../lib/colorCache.js'; import type { Stage } from '../../Stage.js'; import { CoreShaderNode, type CoreShaderType } from '../CoreShaderNode.js'; export type CanvasShaderType< T extends object = Record, C extends object = Record, > = CoreShaderType & { render: ( this: CanvasShaderNode, ctx: CanvasRenderingContext2D, node: CoreNode, renderContext: () => void, ) => void; update?: (this: CanvasShaderNode, node: CoreNode) => void; /** * Set this to true when using ctx functions that scale, clip, rotate, etc.. */ saveAndRestore?: boolean; }; export class CanvasShaderNode< Props extends object = Record, Computed extends object = Record, > extends CoreShaderNode { private updater: ((node: CoreNode, props?: Props) => void) | undefined = undefined; computed: Partial = {}; applySNR: boolean; render: CanvasShaderType['render']; constructor( shaderKey: string, config: CanvasShaderType, stage: Stage, props?: Props, ) { super(shaderKey, config, stage, props); this.applySNR = config.saveAndRestore || false; this.render = config.render; if (config.update !== undefined) { this.updater = config.update!; if (this.props === undefined) { this.updater!(this.node as CoreNode, this.props); return; } this.update = () => { const prevKey = this.valueKey; this.valueKey = this.createValueKey(); if (prevKey === this.valueKey) { return; } if (prevKey.length > 0) { this.stage.shManager.mutateShaderValueUsage(prevKey, -1); } const computed = this.stage.shManager.getShaderValues( this.valueKey, ) as Record; if (computed !== undefined) { this.computed = computed as Computed; return; } this.computed = {}; this.updater!(this.node as CoreNode); this.stage.shManager.setShaderValues(this.valueKey, this.computed); }; } } toColorString(rgba: number) { return normalizeCanvasColor(rgba, true); } }