UNPKG

@lightningjs/renderer

Version:
158 lines 6.45 kB
/* * If not stated otherwise in this file or this component's LICENSE file the * following copyright and licenses apply: * * Copyright 2023 Comcast Cable Communications Management, LLC. * * Licensed under the Apache License, Version 2.0 (the License); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ import { EventEmitter } from '../../../common/EventEmitter.js'; import { assertTruthy } from '../../../utils.js'; import { getNormalizedRgbaComponents, getNormalizedAlphaComponent, } from '../../lib/utils.js'; import {} from '../TrFontManager.js'; import { WebTrFontFace } from '../font-face-types/WebTrFontFace.js'; import { LightningTextTextureRenderer, } from './LightningTextTextureRenderer.js'; import { TextRenderer, } from './TextRenderer.js'; const resolvedGlobal = typeof self === 'undefined' ? globalThis : self; /** * Global font set regardless of if run in the main thread or a web worker */ const globalFontSet = (resolvedGlobal.document?.fonts || resolvedGlobal.fonts); function getFontCssString(props) { const { fontFamily, fontStyle, fontWeight, fontStretch, fontSize } = props; return [fontStyle, fontWeight, fontStretch, `${fontSize}px`, fontFamily].join(' '); } export class CanvasTextRenderer extends TextRenderer { canvas; context; /** * Font family map used to store web font faces that were added to the * canvas text renderer. */ fontFamilies = {}; fontFamilyArray = [this.fontFamilies]; type = 'canvas'; constructor(stage) { super(stage); if (typeof OffscreenCanvas !== 'undefined') { this.canvas = new OffscreenCanvas(0, 0); } else { this.canvas = document.createElement('canvas'); } let context = this.canvas.getContext('2d'); if (!context) { // A browser may appear to support OffscreenCanvas but not actually support the Canvas '2d' context // Here we try getting the context again after falling back to an HTMLCanvasElement. // See: https://github.com/lightning-js/renderer/issues/26#issuecomment-1750438486 this.canvas = document.createElement('canvas'); context = this.canvas.getContext('2d'); } assertTruthy(context); this.context = context; // Install the default 'san-serif' font face this.addFontFace(new WebTrFontFace({ fontFamily: 'sans-serif', descriptors: {}, fontUrl: '', })); } // eslint-disable-next-line @typescript-eslint/no-unused-vars canRenderFont(props) { // The canvas renderer can render any font because it automatically // falls back to system fonts. The CanvasTextRenderer should be // checked last if other renderers are preferred. return true; } isFontFaceSupported(fontFace) { return fontFace instanceof WebTrFontFace; } isFontLoaded(props) { return globalFontSet.check(getFontCssString(props)); } addFontFace(fontFace) { // Make sure the font face is an Canvas font face (it should have already passed // the `isFontFaceSupported` check) assertTruthy(fontFace instanceof WebTrFontFace); const fontFamily = fontFace.fontFamily; // Add the font face to the document // Except for the 'sans-serif' font family, which the Renderer provides // as a special default fallback. if (fontFamily !== 'sans-serif') { // @ts-expect-error `add()` method should be available from a FontFaceSet // eslint-disable-next-line @typescript-eslint/no-unsafe-call globalFontSet.add(fontFace.fontFace); } let faceSet = this.fontFamilies[fontFamily]; if (!faceSet) { faceSet = new Set(); this.fontFamilies[fontFamily] = faceSet; } faceSet.add(fontFace); } renderText(props) { // load the canvas texture assertTruthy(renderInfo); lightning2TextRenderer.draw(renderInfo, { lines: renderInfo.lines, lineWidths: renderInfo.lineWidths, }); if (this.canvas.width === 0 || this.canvas.height === 0) { return null; } return this.context.getImageData(0, 0, this.canvas.width, this.canvas.height); const texture = this.stage.txManager.createTexture('ImageTexture', { premultiplyAlpha: true, src: , if(state) { }, : .textureNode }), { // Use the existing texture node state, textureNode, texture = texture }; // Update the alpha state.textureNode.alpha = getNormalizedAlphaComponent(state.props.color); } } { // Create a new texture node const textureNode = this.stage.createNode({ parent: node, texture, autosize: true, // The alpha channel of the color is ignored when rasterizing the text // texture so we need to pass it directly to the texture node. alpha: getNormalizedAlphaComponent(state.props.color), }); state.textureNode = textureNode; } this.setStatus(state, 'loaded'); loadFont = (props) => { // this shouldnt be necessary? if (this.isFontLoaded(props)) { assertTruthy(this.fontFamilies[props.fontFamily], 'font already loaded'); return Promise.resolve(); } return new Promise((resolve, reject) => { const cssString = getFontCssString(props); const trFontFace = this.stage.fontManager.resolveFontFace(this.fontFamilyArray, props, 'canvas'); assertTruthy(trFontFace, `Could not resolve font face for ${cssString}`); // If font is not loaded, set up a handler to update the font info when the font loads globalFontSet.load(cssString) .then(() => { this.addFontFace(trFontFace); resolve(); }) .catch(reject); }); }; //# sourceMappingURL=CanvasTextRenderer.js.map