UNPKG

jassub

Version:

The Fastest JavaScript SSA/ASS Subtitle Renderer For Browsers

82 lines (66 loc) 2.62 kB
// fallback for browsers that don't support GPU acceleration import type { ASSImage } from '../util.ts' export class Canvas2DRenderer { canvas: OffscreenCanvas | null = null ctx: OffscreenCanvasRenderingContext2D | null = null bufferCanvas = new OffscreenCanvas(1, 1) bufferCtx = this.bufferCanvas.getContext('2d', { alpha: true, desynchronized: true, willReadFrequently: false }) _scheduledResize?: { width: number, height: number } resizeCanvas (width: number, height: number) { if (width <= 0 || height <= 0) return this._scheduledResize = { width, height } } setCanvas (canvas: OffscreenCanvas) { this.canvas = canvas this.ctx = canvas.getContext('2d', { alpha: true, desynchronized: true, willReadFrequently: false }) if (!this.ctx) throw new Error('Could not get 2D context') } setColorMatrix (subtitleColorSpace?: 'BT601' | 'BT709' | 'SMPTE240M' | 'FCC', videoColorSpace?: 'BT601' | 'BT709') {} // this is horribly inefficient, but it's a fallback for systems without a GPU, this is the least of their problems render (images: ASSImage[], heap: Uint8Array): void { if (!this.ctx || !this.canvas) return if (this._scheduledResize) { const { width, height } = this._scheduledResize this._scheduledResize = undefined this.canvas.width = width this.canvas.height = height } else { this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height) } for (const img of images) { if (img.w <= 0 || img.h <= 0) continue const imageData = new ImageData(img.w, img.h) const pixels = new Uint32Array(imageData.data.buffer) const color = ((img.color << 8) & 0xff0000) | ((img.color >> 8) & 0xff00) | ((img.color >> 24) & 0xff) const alpha = (255 - (img.color & 255)) / 255 const stride = img.stride const h = img.h const w = img.w for (let y = h + 1, pos = img.bitmap, res = 0; --y; pos += stride) { for (let z = 0; z < w; ++z, ++res) { const k = heap[pos + z]! if (k !== 0) pixels[res] = ((alpha * k) << 24) | color } } // Draw the ImageData to canvas at the destination position this.bufferCanvas.width = w this.bufferCanvas.height = h this.bufferCtx!.putImageData(imageData, 0, 0) this.ctx.drawImage(this.bufferCanvas, img.dst_x, img.dst_y) } } destroy () { this.ctx = null this.canvas = null this.bufferCtx = null! this.bufferCanvas = null! } }