diff --git a/packages/display/src/displays/webgl/GlyphManager.ts b/packages/display/src/displays/webgl/GlyphManager.ts index c6734985..14530076 100644 --- a/packages/display/src/displays/webgl/GlyphManager.ts +++ b/packages/display/src/displays/webgl/GlyphManager.ts @@ -42,8 +42,28 @@ export type Glyph = { direction: number; // 1(LTR)|0(NEUTRAL)|-1(RTL) } +type Font = { + canvas: HTMLCanvasElement; + textMetricsCache: Map; + ctx: CanvasRenderingContext2D; + scale: number; + glyphs: Map; + baselineOffset: number; + paddingX: number; + offsetX: number; + size: number; + name: string; + width: number; + style: FontStyle; + letterHeight: number; + letterHeightBottom: number; + spaceWidth: number; + paddingY: number; + rowHeight: number +}; + class GlyphManager { - fonts = {}; + private fonts: { [id: string]: Font } = {}; static instance: GlyphManager; @@ -60,7 +80,7 @@ class GlyphManager { // return `${style.font || DEFAULT_FONT}${style.strokeWidth || DEFAULT_STROKE_WIDTH}${style.textAlign || DEFAULT_TEXT_ALIGN}${scale}`; } - initFont(style: FontStyle, scale: number = 1) { + initFont(style: FontStyle, scale: number = 1): Font { const {fonts} = this; const styleId = this.getFontId(style, scale); @@ -100,7 +120,7 @@ class GlyphManager { offsetX: 2 * lineWidth * scale, scale, style, - charWidthCache: new Map(), + textMetricsCache: new Map(), rowHeight: rowHeight * scale, letterHeightBottom: letterHeightBottom, letterHeight: letterHeight, @@ -117,7 +137,7 @@ class GlyphManager { return font.glyphs.has(char); } - getGlyph(char: string, font): Glyph { + getGlyph(char: string, font: Font): Glyph { let glyph = font.glyphs.get(char); if (!glyph) { @@ -128,15 +148,15 @@ class GlyphManager { drawCharacter(font.ctx, char, font.paddingX, font.paddingY, font.style); - let charWidth = font.charWidthCache.get(char); - if (charWidth == undefined) { - charWidth = font.ctx.measureText(char).width; + let metrics = font.textMetricsCache.get(char); + + if (!metrics) { + metrics = font.ctx.measureText(char); } else { - font.charWidthCache.delete(char); + font.textMetricsCache.delete(char); } - - - let width = Math.round((charWidth || 0) + 2 * font.paddingX) * scale; + const {width} = metrics; + const imgWidth = Math.round((width || 0) + 2 * font.paddingX) * scale; // debug only // let lw = font.ctx.lineWidth; @@ -146,14 +166,15 @@ class GlyphManager { // font.ctx.lineWidth = lw; // font.ctx.strokeStyle = GLYPH_STROKE; - let imgData = font.ctx.getImageData(0, 0, width, font.rowHeight); + const imgData = font.ctx.getImageData(0, 0, imgWidth, font.rowHeight); glyph = { + // metrics, char: char, - width: charWidth, + width, data: imgData, direction: getDirection(char.charCodeAt(0)), - advanceX: charWidth ? imgData.width - offsetX : 0 + advanceX: width ? imgData.width - offsetX : 0 }; font.glyphs.set(char, glyph); @@ -171,12 +192,12 @@ class GlyphManager { if (glyph) { width += glyph.width; } else { - let w = font.charWidthCache.get(char); - if (w == undefined) { - w = ctx.measureText(char).width; - font.charWidthCache.set(char, w); + let metrics = font.textMetricsCache.get(char); + if (!metrics) { + metrics = ctx.measureText(char); + font.textMetricsCache.set(char, metrics); } - width += w; + width += metrics.width; } } return width; diff --git a/packages/display/src/displays/webgl/buffer/addText.ts b/packages/display/src/displays/webgl/buffer/addText.ts index 1344834f..1cb54a8e 100644 --- a/packages/display/src/displays/webgl/buffer/addText.ts +++ b/packages/display/src/displays/webgl/buffer/addText.ts @@ -86,8 +86,8 @@ const addText = ( for (let text of lines) { const textData = createTextData(text, glyphAtlas, offsets, textureCoordinates, rotationZ, rotationY); - const tx = textData.width * anchorOffset.x * glyphAtlas.scale * OFFSET_SCALE; const vertexCnt = textData.count * dim; + const tx = (textData.width * anchorOffset.x * glyphAtlas.scale + textData.leftSideBearing) * OFFSET_SCALE; vertex.reserve(vertexCnt); diff --git a/packages/display/src/displays/webgl/buffer/createText.ts b/packages/display/src/displays/webgl/buffer/createText.ts index eeecbed2..f9aa107d 100644 --- a/packages/display/src/displays/webgl/buffer/createText.ts +++ b/packages/display/src/displays/webgl/buffer/createText.ts @@ -40,7 +40,7 @@ const findNextDir = (text, i, glyphAtlas) => { } }; -type TextData = { x: number; x2: number; offset: number; } +type TextData = { x: number; boxWidth: number; offset: number; textWidth: number } const addGlyph = (c: string, glyphAtlas: GlyphAtlas, rotationZ: number, rotationY: number | undefined, positions: FlexArray, texcoords: FlexArray, data: TextData) => { let {offset, x} = data; @@ -54,10 +54,9 @@ const addGlyph = (c: string, glyphAtlas: GlyphAtlas, rotationZ: number, rotation let p = positions.length; const texcoordData = texcoords.data; let t = texcoords.length; - - let rotationHi = rotationZ >> 5; let rotationLow = (rotationZ & 31); + let glyphWidth = 0; if (glyphInfo) { // let {u1, v1, u2, v2, glyph} = glyphInfo; @@ -67,48 +66,53 @@ const addGlyph = (c: string, glyphAtlas: GlyphAtlas, rotationZ: number, rotation let v1 = glyphInfo.v1 << 5 | rotationHi; let v2 = glyphInfo.v2 << 5 | rotationHi; let {advanceX} = glyph; + glyphWidth = glyph.width; + let {width, height} = glyph.data; - let sx = x * OFFSET_SCALE; + + x2 = x + width; + + const scaledX = x * OFFSET_SCALE; + const scaledX2 = x2 * OFFSET_SCALE; height *= OFFSET_SCALE; - x2 = sx + OFFSET_SCALE * width; - positionData[p++] = sx; + positionData[p++] = scaledX; positionData[p++] = 0; if (hasRotY) { positionData[p++] = rotationY; } - positionData[p++] = x2; + positionData[p++] = scaledX2; positionData[p++] = height; if (hasRotY) { positionData[p++] = rotationY; } - positionData[p++] = sx; + positionData[p++] = scaledX; positionData[p++] = height; if (hasRotY) { positionData[p++] = rotationY; } - positionData[p++] = x2; + positionData[p++] = scaledX2; positionData[p++] = 0; if (hasRotY) { positionData[p++] = rotationY; } - positionData[p++] = x2; + positionData[p++] = scaledX2; positionData[p++] = height; if (hasRotY) { positionData[p++] = rotationY; } - positionData[p++] = sx; + positionData[p++] = scaledX; positionData[p++] = 0; if (hasRotY) { @@ -144,7 +148,8 @@ const addGlyph = (c: string, glyphAtlas: GlyphAtlas, rotationZ: number, rotation data.x = x; data.offset = offset; - data.x2 = x2; + data.boxWidth = x2; + data.textWidth += glyphWidth; }; const addText = ( @@ -214,10 +219,11 @@ export const createTextData = ( } - const txtData = { + const txtData: TextData = { x: 0, - x2: 0, - offset: 0 + boxWidth: 0, + offset: 0, + textWidth: 0 }; let baseDirection; let prevDirection; @@ -231,7 +237,11 @@ export const createTextData = ( let char = text.charAt(i); let glyphInfo = glyphAtlas.glyphInfos[char]; let isLast = i == len - 1; - let curDirection = glyphInfo?.glyph?.direction || 0; // -1,0,+1 + let curDirection = 0; + const glyph = glyphInfo?.glyph; + if (glyph) { + curDirection = glyph.direction; // -1,0,+1 + } if (!baseDirection) { if (char == ' ') { // neutral @@ -282,13 +292,13 @@ export const createTextData = ( prevChar = char; } - const {offset, x2} = txtData; + const width = txtData.boxWidth / glyphAtlas.scale; return { - count: offset / 2, + count: txtData.offset / 2, position: positions.data, texcoord: texcoords.data, - width: x2 / OFFSET_SCALE / glyphAtlas.scale - // height: glyphAtlas.letterHeight + width, + leftSideBearing: ( width - txtData.textWidth ) / 2 }; };