fugafacere
Version:
A pure-JS implementation of the W3C's Canvas-2D Context API that can run on top of either Expo Graphics or a browser WebGL context.
156 lines (139 loc) • 4.7 kB
JavaScript
import fntParseASCII from 'parse-bmfont-ascii';
import { getEnvironment } from './environment';
import { Asset } from './utilityObjects';
// Web-only loader code:
async function getWebAsset(name, url) {
return new Promise((resolve, reject) => {
const dom_elem_name = '__expo2dcontext_bmfont_resource_' + name;
let img = document.getElementById(dom_elem_name);
if (img == null) {
img = document.createElement('IMG');
img.id = dom_elem_name;
img.dataset.name = name;
img.onload = () => resolve(img);
img.onerror = (error) => {
reject(error);
};
img.src = url;
} else {
resolve(img);
}
});
}
export class BMFont {
constructor(descriptor_text, image_assets, thresholds) {
this.descriptor = descriptor_text;
if (getEnvironment() === 'expo') {
const wrapped_assets = {};
Object.keys(image_assets).map(function (key, index) {
wrapped_assets[key] = Asset.fromModule(image_assets[key]);
});
this.images = wrapped_assets;
} else {
this.images = image_assets;
}
this.assets_loaded = false;
this.gl_resources = null;
this.thresholds = thresholds;
if (
!('normal' in thresholds) ||
!('bold' in thresholds) ||
!('bolder' in thresholds) ||
!('lighter' in thresholds)
) {
throw new SyntaxError('Missing distance field threshold');
}
}
async await_assets() {
const images = this.images;
if (this.assets_loaded) {
return;
}
if (getEnvironment() === 'expo') {
await Promise.all(
Object.keys(images).map(function (key, index) {
return images[key].downloadAsync();
})
);
} else {
const loaded_assets = await Promise.all(
Object.keys(images).map(function (key, index) {
return getWebAsset(key, images[key]);
})
);
for (let i = 0; i < loaded_assets.length; i++) {
this.images[loaded_assets[i].dataset.name] = loaded_assets[i];
}
}
this.assets_loaded = true;
}
initialize_gl_resources(gl) {
if (this.gl_resources !== null) {
return this.gl_resources;
}
if (!this.assets_loaded) {
throw new ReferenceError('Must load font assets before initializing GL resources');
}
const bmfont_descriptor = fntParseASCII(this.descriptor);
const texture_array = gl.createTexture();
gl.bindTexture(gl.TEXTURE_2D_ARRAY, texture_array);
gl.texStorage3D(
gl.TEXTURE_2D_ARRAY,
1,
gl.RGBA8,
bmfont_descriptor.common.scaleW,
bmfont_descriptor.common.scaleH,
bmfont_descriptor.common.pages
);
gl.texParameteri(gl.TEXTURE_2D_ARRAY, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
gl.texParameteri(gl.TEXTURE_2D_ARRAY, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
gl.texParameteri(gl.TEXTURE_2D_ARRAY, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
gl.texParameteri(gl.TEXTURE_2D_ARRAY, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
for (let i = 0; i < bmfont_descriptor.pages.length; i++) {
const page_texture = this.images[bmfont_descriptor.pages[i]];
if (page_texture) {
gl.texSubImage3D(
gl.TEXTURE_2D_ARRAY,
0,
0,
0,
i,
bmfont_descriptor.common.scaleW,
bmfont_descriptor.common.scaleH,
1,
gl.RGBA,
gl.UNSIGNED_BYTE,
page_texture
);
} else {
throw new ReferenceError(
'Glyph page "' + bmfont_descriptor.pages[i] + '" not found in provided assets'
);
}
}
const font_descriptor = {};
font_descriptor.textures = texture_array;
font_descriptor.chars = {};
bmfont_descriptor.chars.map((char) => {
font_descriptor.chars[String.fromCharCode(char.id)] = char;
char.u1 = char.x / bmfont_descriptor.common.scaleW;
char.v1 = char.y / bmfont_descriptor.common.scaleH;
char.u2 = char.u1 + char.width / bmfont_descriptor.common.scaleW;
char.v2 = char.v1 + char.height / bmfont_descriptor.common.scaleH;
});
font_descriptor.kernings = {};
bmfont_descriptor.kernings.map((kerning) => {
const first = String.fromCharCode(kerning.first);
const second = String.fromCharCode(kerning.second);
if (!(first in font_descriptor.kernings)) {
font_descriptor.kernings[first] = {};
}
font_descriptor.kernings[first][second] = kerning.amount;
});
font_descriptor.info = bmfont_descriptor.info;
font_descriptor.common = bmfont_descriptor.common;
font_descriptor.info.thresholds = this.thresholds;
this.gl_resources = font_descriptor;
return this.gl_resources;
}
}