Skip to content

Commit

Permalink
refactor(driver): text measure
Browse files Browse the repository at this point in the history
  • Loading branch information
atty303 committed May 16, 2024
1 parent 835e1f3 commit e6074c5
Show file tree
Hide file tree
Showing 2 changed files with 52 additions and 49 deletions.
89 changes: 45 additions & 44 deletions packages/driver/src/js/renderer/text.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,62 +3,50 @@ import type { TextureBitmap } from "./renderer.ts";

const reColorGlobal = /\^([0-9])|\^[xX]([0-9a-fA-F]{6})/g;

interface WorkerGlobalScope {
fonts: FontFaceSet;
export async function loadFonts() {
await loadFont("/LiberationSans-Regular.woff", "Liberation Sans");
await loadFont("/LiberationSans-Bold.woff", "Liberation Sans Bold");
await loadFont("/VeraMono.woff", "Bitstream Vera Mono");
}

export class TextRasterizer {
static async loadFonts() {
await TextRasterizer.loadFont("/LiberationSans-Regular.woff", "Liberation Sans");
await TextRasterizer.loadFont("/LiberationSans-Bold.woff", "Liberation Sans Bold");
await TextRasterizer.loadFont("/VeraMono.woff", "Bitstream Vera Mono");
}

static async loadFont(url: string, family: string) {
const r = await fetch(url);
if (!r.ok) throw new Error(`Failed to load font: ${url}`);
const blob = await r.blob();
const fontFace = new FontFace(family, await blob.arrayBuffer());
await fontFace.load();
(self as unknown as WorkerGlobalScope).fonts.add(fontFace);
}
async function loadFont(url: string, family: string) {
const r = await fetch(url);
if (!r.ok) throw new Error(`Failed to load font: ${url}`);
const blob = await r.blob();
const fontFace = new FontFace(family, await blob.arrayBuffer());
await fontFace.load();
(self as unknown as { fonts: FontFaceSet }).fonts.add(fontFace);
}

private static font(size: number, fontNum: number) {
const fontSize = size - 2;
switch (fontNum) {
case 1:
return `${fontSize}px Liberation Sans`;
case 2:
return `${fontSize}px Liberation Sans Bold`;
default:
return `${fontSize}px Bitstream Vera Mono`;
}
function font(size: number, fontNum: number) {
const fontSize = size - 2;
switch (fontNum) {
case 1:
return `${fontSize}px Liberation Sans`;
case 2:
return `${fontSize}px Liberation Sans Bold`;
default:
return `${fontSize}px Bitstream Vera Mono`;
}
}

private readonly cache: Map<string, { width: number; bitmap: TextureBitmap | undefined }> = new Map();
export class TextMetrics {
private readonly context;

constructor() {
const canvas = new OffscreenCanvas(1, 1);
const gl = canvas.getContext("webgl");
if (gl) {
const maxTextureSize = gl.getParameter(gl.MAX_TEXTURE_SIZE);
console.log("maxTextureSize", maxTextureSize);
}

const canvas1 = new OffscreenCanvas(1, 1);
const context = canvas1.getContext("2d");
const context = canvas.getContext("2d");
if (!context) throw new Error("Failed to get 2D context");
this.context = context;
}

measureText(size: number, font: number, text: string) {
this.context.font = TextRasterizer.font(size, font);
measure(size: number, fontNum: number, text: string) {
this.context.font = font(size, fontNum);
return this.context.measureText(text.replaceAll(reColorGlobal, "")).width;
}

measureTextCursorIndex(size: number, font: number, text: string, cursorX: number, cursorY: number) {
this.context.font = TextRasterizer.font(size, font);
measureCursorIndex(size: number, fontNum: number, text: string, cursorX: number, cursorY: number) {
this.context.font = font(size, fontNum);
const lines = text.split("\n");
const y = Math.floor(Math.max(0, Math.min(lines.length - 1, cursorY / size)));
const line = lines[y];
Expand All @@ -74,17 +62,30 @@ export class TextRasterizer {
}
return i;
}
}

export class TextRasterizer {
private readonly cache: Map<string, { width: number; bitmap: TextureBitmap | undefined }> = new Map();
private readonly maxTextureSize: number;

constructor(readonly textMetrics: TextMetrics) {
const canvas = new OffscreenCanvas(1, 1);
const gl = canvas.getContext("webgl");
if (gl) {
this.maxTextureSize = gl.getParameter(gl.MAX_TEXTURE_SIZE) as number;
}
}

get(size: number, font: number, text: string) {
const key = `${size}:${font}:${text}`;
get(size: number, fontNum: number, text: string) {
const key = `${size}:${fontNum}:${text}`;
let bitmap = this.cache.get(key);
if (!bitmap) {
const width = this.measureText(size, font, text);
const width = this.textMetrics.measure(size, fontNum, text);
if (width > 0) {
const canvas = new OffscreenCanvas(width, size);
const context = canvas.getContext("2d");
if (!context) throw new Error("Failed to get 2D context");
context.font = TextRasterizer.font(size, font);
context.font = font(size, fontNum);
context.fillStyle = "white";
context.textBaseline = "bottom";
context.fillText(text, 0, size);
Expand Down
12 changes: 7 additions & 5 deletions packages/driver/src/js/worker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { ImageRepository } from "./image";
// @ts-ignore
import { createNODEFS } from "./nodefs.js";
import { Renderer, TextRasterizer, WebGL1Backend } from "./renderer";
import { TextMetrics, loadFonts } from "./renderer/text.ts";

interface DriverModule extends EmscriptenModule {
FS: typeof FS;
Expand Down Expand Up @@ -50,6 +51,7 @@ type Imports = {

export class DriverWorker {
private imageRepo: ImageRepository | undefined;
private textMetrics: TextMetrics | undefined;
private textRasterizer: TextRasterizer | undefined;
private renderer: Renderer | undefined;
private screenSize: { width: number; height: number } = {
Expand Down Expand Up @@ -80,8 +82,9 @@ export class DriverWorker {
) {
this.imageRepo = new ImageRepository(`${assetPrefix}/root/`);

await TextRasterizer.loadFonts();
this.textRasterizer = new TextRasterizer();
await loadFonts();
this.textMetrics = new TextMetrics();
this.textRasterizer = new TextRasterizer(this.textMetrics);

this.renderer = new Renderer(this.imageRepo, this.textRasterizer, this.screenSize);
this.hostCallbacks = {
Expand Down Expand Up @@ -280,10 +283,9 @@ export class DriverWorker {
this.renderer?.render(new DataView(module.HEAPU8.buffer, bufferPtr, size));
}
},
getStringWidth: (size: number, font: number, text: string) =>
this.textRasterizer?.measureText(size, font, text) ?? 0,
getStringWidth: (size: number, font: number, text: string) => this.textMetrics?.measure(size, font, text) ?? 0,
getStringCursorIndex: (size: number, font: number, text: string, cursorX: number, cursorY: number) =>
this.textRasterizer?.measureTextCursorIndex(size, font, text, cursorX, cursorY) ?? 0,
this.textMetrics?.measureCursorIndex(size, font, text, cursorX, cursorY) ?? 0,
copy: (text: string) => this.mainCallbacks?.copy(text),
paste: () => this.mainCallbacks?.paste(),
openUrl: (url: string) => this.mainCallbacks?.openUrl(url),
Expand Down

0 comments on commit e6074c5

Please sign in to comment.