UNPKG

@lightningtv/renderer

Version:
212 lines 7.4 kB
import { SubTexture } from '../../textures/SubTexture.js'; import { TextureType } from '../../textures/Texture.js'; import { CoreRenderer, } from '../CoreRenderer.js'; import { CanvasTexture } from './CanvasTexture.js'; import { parseColor, parseToAbgrString, parseToRgbaString, } from './internal/ColorUtils.js'; import { assertTruthy } from '../../../utils.js'; import { CanvasShaderNode } from './CanvasShaderNode.js'; export class CanvasRenderer extends CoreRenderer { context; canvas; pixelRatio; clearColor; renderToTextureActive = false; activeRttNode = null; parsedColorCache = new Map(); constructor(options) { super(options); this.mode = 'canvas'; const { canvas } = options; this.canvas = canvas; this.context = canvas.getContext('2d'); this.pixelRatio = this.stage.pixelRatio; this.clearColor = this.getParsedColor(this.stage.clearColor); } reset() { this.canvas.width = this.canvas.width; // quick reset canvas const ctx = this.context; if (this.clearColor) { ctx.fillStyle = this.clearColor; ctx.fillRect(0, 0, this.canvas.width, this.canvas.height); } ctx.scale(this.pixelRatio, this.pixelRatio); } render() { // noop } addQuad(quad) { const ctx = this.context; const { tx, ty, ta, tb, tc, td, clippingRect } = quad; let texture = quad.texture; const textureType = texture?.type; assertTruthy(textureType, 'Texture type is not defined'); // The Canvas2D renderer only supports image and color textures if (textureType !== TextureType.image && textureType !== TextureType.color && textureType !== TextureType.subTexture && textureType !== TextureType.noise) { return; } if (texture) { if (texture instanceof SubTexture) { texture = texture.parentTexture; } if (texture.state === 'freed') { return; } if (texture.state !== 'loaded') { return; } } const hasTransform = ta !== 1; const hasClipping = clippingRect.width !== 0 && clippingRect.height !== 0; const hasShader = quad.shader !== null; let saveAndRestore = hasTransform === true || hasClipping === true; if (hasShader === true) { saveAndRestore = saveAndRestore || quad.shader.applySNR; } if (saveAndRestore) { ctx.save(); } if (hasClipping === true) { const path = new Path2D(); const { x, y, width, height } = clippingRect; path.rect(x, y, width, height); ctx.clip(path); } if (hasTransform === true) { // Quad transform: // | ta tb tx | // | tc td ty | // | 0 0 1 | // C2D transform: // | a c e | // | b d f | // | 0 0 1 | const scale = this.pixelRatio; ctx.setTransform(ta, tc, tb, td, tx * scale, ty * scale); ctx.scale(scale, scale); ctx.translate(-tx, -ty); } if (hasShader === true) { let renderContext = () => { this.renderContext(quad); }; quad.shader.render(ctx, quad, renderContext); renderContext = null; } else { this.renderContext(quad); } if (saveAndRestore) { ctx.restore(); } } renderContext(quad) { const color = quad.colorTl; const textureType = quad.texture?.type; if ((textureType === TextureType.image || textureType === TextureType.subTexture || textureType === TextureType.noise) && quad.texture?.ctxTexture) { const tintColor = parseColor(color); const image = quad.texture.ctxTexture.getImage(tintColor); this.context.globalAlpha = tintColor.a ?? quad.alpha; if (textureType === TextureType.subTexture) { this.context.drawImage(image, quad.texture.props.x, quad.texture.props.y, quad.texture.props.width, quad.texture.props.height, quad.tx, quad.ty, quad.width, quad.height); } else { try { this.context.drawImage(image, quad.tx, quad.ty, quad.width, quad.height); // eslint-disable-next-line @typescript-eslint/no-unused-vars } catch (error) { // noop } } this.context.globalAlpha = 1; return; } const hasGradient = quad.colorTl !== quad.colorTr || quad.colorTl !== quad.colorBr; if (textureType === TextureType.color && hasGradient) { let endX = quad.tx; let endY = quad.ty; let endColor; if (quad.colorTl === quad.colorTr) { // vertical endX = quad.tx; endY = quad.ty + quad.height; endColor = quad.colorBr; } else { // horizontal endX = quad.tx + quad.width; endY = quad.ty; endColor = quad.colorTr; } const gradient = this.context.createLinearGradient(quad.tx, quad.ty, endX, endY); gradient.addColorStop(0, this.getParsedColor(color)); gradient.addColorStop(1, this.getParsedColor(endColor)); this.context.fillStyle = gradient; this.context.fillRect(quad.tx, quad.ty, quad.width, quad.height); } else if (textureType === TextureType.color) { this.context.fillStyle = this.getParsedColor(color); this.context.fillRect(quad.tx, quad.ty, quad.width, quad.height); } } createShaderNode(shaderKey, shaderType, props) { return new CanvasShaderNode(shaderKey, shaderType, this.stage, props); } createShaderProgram(shaderConfig) { return null; } supportsShaderType(shaderType) { return shaderType.render !== undefined; } createCtxTexture(textureSource) { return new CanvasTexture(this.stage.txMemManager, textureSource); } renderRTTNodes() { // noop } removeRTTNode(node) { // noop } renderToTexture(node) { // noop } getBufferInfo() { return null; } getQuadCount() { return null; } getParsedColor(color, isRGBA = false) { let out = this.parsedColorCache.get(color); if (out !== undefined) { return out; } if (isRGBA) { out = parseToRgbaString(color); } else { out = parseToAbgrString(color); } this.parsedColorCache.set(color, out); return out; } /** * Updates the clear color of the canvas renderer. * * @param color - The color to set as the clear color. */ updateClearColor(color) { this.clearColor = this.getParsedColor(color); } getDefaultShaderNode() { return null; } } //# sourceMappingURL=CanvasRenderer.js.map