import { Texture } from './Texture.js'; export class TextRenderer { constructor(gl) { this.gl = gl; this.canvas = document.createElement('canvas'); this.ctx = this.canvas.getContext('2d'); this._cachedKey = null; this._cachedEntry = null; } draw(renderer, text, x, y, options = {}) { const entry = this._resolve(text, options); renderer.drawTexture(entry.texture, x, y, entry.width, entry.height); return entry; } measure(text, options = {}) { return this._resolve(text, options); } _resolve(text, options = {}) { const key = this._makeKey(text, options); if (this._cachedKey !== key) { this._flushCache(); this._cachedKey = key; this._cachedEntry = this._rasterize(text, options); } return this._cachedEntry; } _makeKey(text, { size = 20, font = 'arial', fillStyle = 'black', bold = true }) { return `${text}|${size}|${font}|${fillStyle}|${bold}`; } _rasterize(text, { size = 20, font = 'arial', fillStyle = 'black', bold = true }) { const { ctx, canvas } = this; const pad = 4; const fontString = `${bold ? 'bold ' : ''}${size}px ${font}`; ctx.font = fontString; const textWidth = Math.ceil(ctx.measureText(text).width); const width = textWidth + pad * 2; const height = Math.ceil(size * 1.3) + pad * 2; const scale = Math.min(window.devicePixelRatio || 1, 2); canvas.width = Math.max(1, Math.ceil(width * scale)); canvas.height = Math.max(1, Math.ceil(height * scale)); ctx.setTransform(scale, 0, 0, scale, 0, 0); ctx.clearRect(0, 0, width, height); ctx.font = fontString; ctx.fillStyle = fillStyle; ctx.textAlign = 'left'; ctx.textBaseline = 'alphabetic'; ctx.fillText(text, pad, pad + size); const texture = new Texture(this.gl, canvas.width, canvas.height); texture.uploadCanvas(canvas); return { texture, width, height }; } _flushCache() { if (this._cachedEntry) { this._cachedEntry.texture.dispose(); this._cachedEntry = null; } } }