UNPKG

@lightningjs/renderer

Version:
209 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 } from '../../lib/colorParser.js'; import { CanvasShaderNode } from './CanvasShaderNode.js'; import { normalizeCanvasColor } from '../../lib/colorCache.js'; export class CanvasRenderer extends CoreRenderer { context; canvas; pixelRatio; clearColor; renderToTextureActive = false; activeRttNode = null; constructor(stage) { super(stage); this.mode = 'canvas'; const platform = stage.platform; const canvas = platform.canvas; this.canvas = canvas; this.context = canvas.getContext('2d'); this.pixelRatio = stage.pixelRatio; this.clearColor = normalizeCanvasColor(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(node) { const ctx = this.context; const { tx, ty, ta, tb, tc, td } = node.globalTransform; const clippingRect = node.clippingRect; let texture = (node.props.texture || this.stage.defaultTexture); // The Canvas2D renderer only supports image textures, no textures are used for color blocks if (texture !== null) { const textureType = texture.type; if (textureType !== TextureType.image && textureType !== TextureType.subTexture && textureType !== TextureType.color && textureType !== TextureType.noise) { return; } } const hasTransform = ta !== 1; const clippingValid = clippingRect.valid === true; // If the clipping rect is valid but zero-area, the node is fully clipped — skip rendering if (clippingValid === true && clippingRect.w === 0 && clippingRect.h === 0) { return; } const hasClipping = clippingValid === true && clippingRect.w !== 0 && clippingRect.h !== 0; const shader = node.props.shader; const hasShader = shader !== null; let saveAndRestore = hasTransform === true || hasClipping === true; if (hasShader === true) { saveAndRestore = saveAndRestore || shader.applySNR; } if (saveAndRestore) { ctx.save(); } if (hasClipping === true) { const path = new Path2D(); const { x, y, w, h } = clippingRect; path.rect(x, y, w, h); 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(node, texture); }; shader.render(ctx, node, renderContext); renderContext = null; } else { this.renderContext(node, texture); } if (saveAndRestore) { ctx.restore(); } } renderContext(node, texture) { const color = node.premultipliedColorTl; const textureType = texture.type; const tx = node.globalTransform.tx; const ty = node.globalTransform.ty; const width = node.props.w; const height = node.props.h; if (textureType !== TextureType.color) { const tintColor = parseColor(color); if (textureType !== TextureType.subTexture) { const image = texture.ctxTexture.getImage(tintColor); if (image === null) { return; } this.context.globalAlpha = tintColor.a ?? node.worldAlpha; this.context.drawImage(image, tx, ty, width, height); this.context.globalAlpha = 1; return; } const image = texture.parentTexture.ctxTexture.getImage(tintColor); if (image === null) { return; } const props = texture.props; this.context.globalAlpha = tintColor.a ?? node.worldAlpha; this.context.drawImage(image, props.x, props.y, props.w, props.h, tx, ty, width, height); this.context.globalAlpha = 1; return; } const hasGradient = node.premultipliedColorTl !== node.premultipliedColorTr || node.premultipliedColorTl !== node.premultipliedColorBr; if (hasGradient === true) { let endX = tx; let endY = ty; let endColor; if (node.premultipliedColorTl === node.premultipliedColorTr) { // vertical endX = tx; endY = ty + height; endColor = node.premultipliedColorBr; } else { // horizontal endX = tx + width; endY = ty; endColor = node.premultipliedColorTr; } const gradient = this.context.createLinearGradient(tx, ty, endX, endY); gradient.addColorStop(0, normalizeCanvasColor(color)); gradient.addColorStop(1, normalizeCanvasColor(endColor)); this.context.fillStyle = gradient; this.context.fillRect(tx, ty, width, height); } else { this.context.fillStyle = normalizeCanvasColor(color); this.context.fillRect(tx, ty, width, 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; } /** * Updates the clear color of the canvas renderer. * * @param color - The color to set as the clear color. */ updateClearColor(color) { this.clearColor = normalizeCanvasColor(color); } updateViewport() { // noop } getDefaultShaderNode() { return null; } destroy() { // Release canvas 2D context by resizing canvas to 0 this.canvas.width = 0; this.canvas.height = 0; } } //# sourceMappingURL=CanvasRenderer.js.map