playcanvas
Version:
Open-source WebGL/WebGPU 3D engine for the web
115 lines (114 loc) • 3.3 kB
JavaScript
import { math } from "../../core/math/math.js";
import { Texture } from "../../platform/graphics/texture.js";
import { FILTER_NEAREST } from "../../platform/graphics/constants.js";
class WordAtlas {
constructor(device, words) {
const initContext = (context2) => {
context2.font = '10px "Lucida Console", Monaco, monospace';
context2.textAlign = "left";
context2.textBaseline = "alphabetic";
};
const isNumber = (word) => {
return word === "." || word.length === 1 && word.charCodeAt(0) >= 48 && word.charCodeAt(0) <= 57;
};
const canvas = document.createElement("canvas");
const context = canvas.getContext("2d", { alpha: true });
initContext(context);
const placements = /* @__PURE__ */ new Map();
const padding = 5;
const width = 512;
let x = padding;
let y = padding;
words.forEach((word) => {
const measurement = context.measureText(word);
const l = Math.ceil(-measurement.actualBoundingBoxLeft);
const r = Math.ceil(measurement.actualBoundingBoxRight);
const a = Math.ceil(measurement.actualBoundingBoxAscent);
const d = Math.ceil(measurement.actualBoundingBoxDescent);
const w = l + r;
const h = a + d;
if (x + w + padding >= width) {
x = padding;
y += 16;
}
placements.set(word, { l, r, a, d, w, h, x, y });
x += w + padding;
});
canvas.width = 512;
canvas.height = math.nextPowerOfTwo(y + 16 + padding);
initContext(context);
context.fillStyle = "rgb(0, 0, 0)";
context.fillRect(0, 0, canvas.width, canvas.height);
placements.forEach((m, word) => {
context.fillStyle = isNumber(word) ? "rgb(255, 240, 100)" : "rgb(150, 220, 230)";
context.fillText(word, m.x - m.l, m.y + m.a);
});
this.placements = placements;
const data = context.getImageData(0, 0, canvas.width, canvas.height).data;
for (let i = 0; i < data.length; i += 4) {
const maxChannel = Math.max(data[i + 0], data[i + 1], data[i + 2]);
data[i + 3] = Math.min(maxChannel * 2, 255);
}
this.texture = new Texture(device, {
name: "mini-stats-word-atlas",
width: canvas.width,
height: canvas.height,
mipmaps: false,
minFilter: FILTER_NEAREST,
magFilter: FILTER_NEAREST,
levels: [data]
});
}
destroy() {
this.texture.destroy();
this.texture = null;
}
render(render2d, word, x, y) {
const p = this.placements.get(word);
if (p) {
const padding = 1;
render2d.quad(
x + p.l - padding,
y - p.d + padding,
p.w + padding * 2,
p.h + padding * 2,
p.x - padding,
this.texture.height - p.y - p.h - padding,
void 0,
void 0,
this.texture,
1
);
return p.w;
}
let totalWidth = 0;
for (let i = 0; i < word.length; i++) {
const char = word[i];
if (char === " ") {
totalWidth += 5;
continue;
}
const charPlacement = this.placements.get(char);
if (charPlacement) {
const padding = 1;
render2d.quad(
x + totalWidth + charPlacement.l - padding,
y - charPlacement.d + padding,
charPlacement.w + padding * 2,
charPlacement.h + padding * 2,
charPlacement.x - padding,
this.texture.height - charPlacement.y - charPlacement.h - padding,
void 0,
void 0,
this.texture,
1
);
totalWidth += charPlacement.w;
}
}
return totalWidth;
}
}
export {
WordAtlas
};