add new font
This commit is contained in:
parent
21234a882d
commit
4ff00404d4
194
font_maker/font_maker.js
Normal file
194
font_maker/font_maker.js
Normal file
@ -0,0 +1,194 @@
|
||||
const canvas = document.querySelector("#canvas");
|
||||
const selectedDiv = document.querySelector("#selected");
|
||||
const charlistTable = document.querySelector("#charlist");
|
||||
const exportButton = document.querySelector("#export");
|
||||
const exportAreaCode = document.querySelector("#export-area");
|
||||
|
||||
const [width, height] = [400, 400];
|
||||
|
||||
canvas.width = width;
|
||||
canvas.height = height;
|
||||
|
||||
canvas.style.width = width + "px";
|
||||
canvas.style.height = height + "px";
|
||||
|
||||
const g = canvas.getContext("2d");
|
||||
g.imageSmoothingEnabled = false;
|
||||
|
||||
const state = new Array(64).fill(false);
|
||||
|
||||
function render() {
|
||||
g.fillStyle = "black";
|
||||
g.fillRect(0, 0, width, height);
|
||||
g.fillStyle = "white";
|
||||
for (let y = 0; y < 8; ++y) {
|
||||
for (let x = 0; x < 8; ++x) {
|
||||
if (state[y * 8 + x]) {
|
||||
g.fillRect(
|
||||
x * (width / 8),
|
||||
y * (height / 8),
|
||||
width / 8,
|
||||
height / 8,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
renderGrid();
|
||||
}
|
||||
|
||||
function renderGrid() {
|
||||
g.strokeStyle = "gray";
|
||||
g.lineWidth = 1;
|
||||
g.beginPath();
|
||||
for (let y = 0; y < 8; ++y) {
|
||||
for (let x = 0; x < 8; ++x) {
|
||||
g.moveTo(x * (width / 8), 0);
|
||||
g.lineTo(x * (width / 8), height);
|
||||
g.moveTo(0, y * (height / 8));
|
||||
g.lineTo(width, y * (height / 8));
|
||||
}
|
||||
}
|
||||
g.moveTo(width - 1, 0);
|
||||
g.lineTo(width - 1, height);
|
||||
g.moveTo(0, height - 1);
|
||||
g.lineTo(width, height - 1);
|
||||
g.stroke();
|
||||
}
|
||||
|
||||
const specialChars = Object.fromEntries(
|
||||
Object.entries({
|
||||
"\0": "0",
|
||||
"\n": "n",
|
||||
"\r": "r",
|
||||
"\t": "t",
|
||||
}).map(([ch, r]) => [ch.charCodeAt(0), "\\" + r]),
|
||||
);
|
||||
|
||||
const chars = new Array(127).fill(0).map((_, i) => ({
|
||||
i,
|
||||
ch: specialChars[i] ?? (i > 31 ? String.fromCharCode(i) : ""),
|
||||
hex: new Array(8).fill(0),
|
||||
}));
|
||||
|
||||
let selected = { i: 0, ch: "\0", hex: new Array(8).fill(0) };
|
||||
|
||||
function stateToHex(state) {
|
||||
let hex = new Array(8).fill(0);
|
||||
for (let y = 0; y < 8; ++y) {
|
||||
let byte = 0;
|
||||
for (let x = 0; x < 8; ++x) {
|
||||
if (state[y * 8 + x]) {
|
||||
byte |= 1 << 7 - x;
|
||||
}
|
||||
}
|
||||
hex[y] = byte;
|
||||
}
|
||||
return hex;
|
||||
}
|
||||
|
||||
function stateFromHex(state, hex) {
|
||||
for (let y = 0; y < 8; ++y) {
|
||||
for (let x = 0; x < 8; ++x) {
|
||||
state[y * 8 + x] = (hex[y] >> 7 - x & 1) != 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function html(strings, ...values) {
|
||||
return strings[0] +
|
||||
strings.slice(1).map((v, i) => values[i].toString() + v).join("");
|
||||
}
|
||||
|
||||
function renderSelectedChar() {
|
||||
const { i, ch, hex } = selected;
|
||||
selectedDiv.innerHTML = html`
|
||||
<span><code>${i}</code></span>
|
||||
<span><code>${ch}</code></span>
|
||||
<span><code>0x${hex.map((v) => v.toString(16).padStart(2, "0")).join(
|
||||
"",
|
||||
)}</code></span>
|
||||
<span><input type="text" id="overwrite"></span>
|
||||
<span><button id="selected-save">Save</button></span>
|
||||
`;
|
||||
|
||||
document.querySelector("#selected-save")
|
||||
.onclick = () => {
|
||||
const overwrite = document.querySelector("#overwrite").value;
|
||||
if (overwrite) {
|
||||
const hex = new Array(8).fill(0);
|
||||
for (let i = 0; i < 8; ++i) {
|
||||
hex[i] = parseInt(
|
||||
overwrite.slice(i * 2 + 2, i * 2 + 4),
|
||||
16,
|
||||
);
|
||||
}
|
||||
chars[selected.i].hex = hex;
|
||||
selected.hex = hex.map((v) => v);
|
||||
stateFromHex(state, hex);
|
||||
renderCharlist();
|
||||
renderSelectedChar();
|
||||
render();
|
||||
} else {
|
||||
chars[selected.i].hex = selected.hex.map((v) => v);
|
||||
renderCharlist();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
function renderCharlist() {
|
||||
charlistTable.innerHTML = html`
|
||||
<tr>
|
||||
<td>int</td>
|
||||
<td>char</td>
|
||||
<td>hex</td>
|
||||
<td>action</td>
|
||||
</tr>
|
||||
` + chars
|
||||
.map(({ i, ch, hex }) =>
|
||||
html`
|
||||
<tr>
|
||||
<td><code>${i}</code></td>
|
||||
<td><code>${ch}</code></td>
|
||||
<td><code>0x${hex.map((v) =>
|
||||
v.toString(16).padStart(2, "0")
|
||||
).join("")}</code></td>
|
||||
<td><button id="char-select-${i}">Select</button></td>
|
||||
</tr>
|
||||
`
|
||||
).join("");
|
||||
|
||||
for (const char of chars) {
|
||||
const { i, ch, hex } = char;
|
||||
document.querySelector(`#char-select-${i}`)
|
||||
.onclick = () => {
|
||||
selected = { i, ch, hex: hex.map((v) => v) };
|
||||
renderSelectedChar();
|
||||
stateFromHex(state, selected.hex);
|
||||
render();
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
canvas.onclick = (ev) => {
|
||||
const [x, y] = [ev.offsetX / width * 8, ev.offsetY / height * 8].map(
|
||||
Math.floor,
|
||||
);
|
||||
state[y * 8 + x] = !state[y * 8 + x];
|
||||
render();
|
||||
selected.hex = stateToHex(state);
|
||||
renderSelectedChar();
|
||||
};
|
||||
|
||||
renderSelectedChar();
|
||||
render();
|
||||
renderCharlist();
|
||||
|
||||
exportButton.onclick = () => {
|
||||
exportAreaCode.innerText = chars.map(({ i, ch, hex }) =>
|
||||
`
|
||||
case ${ch != "" ? `'${ch}'` : i}: return 0x${
|
||||
hex.map((v) => v.toString(16).padStart(2, "0")).join("")
|
||||
};
|
||||
`.trim()
|
||||
).join("\n");
|
||||
};
|
||||
52
font_maker/index.html
Normal file
52
font_maker/index.html
Normal file
@ -0,0 +1,52 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Document</title>
|
||||
<script src="./font_maker.js" type="module" defer></script>
|
||||
|
||||
<style>
|
||||
:root {
|
||||
color-scheme: light dark;
|
||||
}
|
||||
* {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
table {
|
||||
border-collapse: collapse;
|
||||
}
|
||||
td {
|
||||
border-top: 1px solid gray;
|
||||
min-width: 70px;
|
||||
text-align: center;
|
||||
padding: 4px;
|
||||
}
|
||||
#selected {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
border-top: 1px solid gray;
|
||||
padding: 10px;
|
||||
}
|
||||
#selected > * {
|
||||
min-width: 70px;
|
||||
text-align: center;
|
||||
}
|
||||
code {
|
||||
font-size: 18px;
|
||||
background-color: #444;
|
||||
padding: 2px;
|
||||
padding-left: 4px;
|
||||
padding-right: 4px;
|
||||
border-radius: 2px;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<canvas id="canvas"></canvas>
|
||||
<div id="selected"></div>
|
||||
<table id="charlist"></table>
|
||||
<button id="export">Export</button>
|
||||
<pre><code id="export-area"></code></pre>
|
||||
</body>
|
||||
</html>
|
||||
@ -20,6 +20,21 @@ constexpr uint64_t char_data(uint8_t ch)
|
||||
switch (ch) {
|
||||
// clang-format off
|
||||
case ' ': return 0x0000000000000000;
|
||||
case '!': return 0x0018181818001818;
|
||||
case '"': return 0x0066660000000000;
|
||||
case '#': return 0x00247e24247e2400;
|
||||
case '$': return 0x00187e6218467e18;
|
||||
case '%': return 0x0022562c18346a44;
|
||||
case '&': return 0x00784c38724a4638;
|
||||
case '\'': return 0x0018180000000000;
|
||||
case '(': return 0x000c18303030180c;
|
||||
case ')': return 0x0030180c0c0c1830;
|
||||
case '*': return 0x0000241818240000;
|
||||
case '+': return 0x000018187e7e1818;
|
||||
case ',': return 0x0000000000181830;
|
||||
case '-': return 0x000000007e7e0000;
|
||||
case '.': return 0x0000000000181800;
|
||||
case '/': return 0x0002060c18306040;
|
||||
|
||||
case '0': return 0x003C666E7666663C;
|
||||
case '1': return 0x001838181818183C;
|
||||
@ -32,6 +47,14 @@ constexpr uint64_t char_data(uint8_t ch)
|
||||
case '8': return 0x003C66663C66663C;
|
||||
case '9': return 0x003C66663E06663C;
|
||||
|
||||
case ':': return 0x0018180000181800;
|
||||
case ';': return 0x0000181800181830;
|
||||
case '<': return 0x00060c1830180c06;
|
||||
case '=': return 0x00007e7e007e7e00;
|
||||
case '>': return 0x006030180c183060;
|
||||
case '?': return 0x003e660c18001818;
|
||||
case '@': return 0x003c664a564e603c;
|
||||
|
||||
case 'A': return 0x00187E66667E6666;
|
||||
case 'B': return 0x00787E667C667E7C;
|
||||
case 'C': return 0x003C7E6060607E3C;
|
||||
@ -59,7 +82,42 @@ constexpr uint64_t char_data(uint8_t ch)
|
||||
case 'Y': return 0x0066663C18181818;
|
||||
case 'Z': return 0x007E7E0C18307E7E;
|
||||
|
||||
case ':': return 0x0018180000181800;
|
||||
case '[': return 0x003c30303030303c;
|
||||
case '\\': return 0x00406030180c0602;
|
||||
case ']': return 0x003c0c0c0c0c0c3c;
|
||||
case '^': return 0x0010386c44000000;
|
||||
case '_': return 0x00000000000000ff;
|
||||
case '`': return 0x0030181800000000;
|
||||
case 'a': return 0x0000003c023e423e;
|
||||
case 'b': return 0x004040407c42427c;
|
||||
case 'c': return 0x000000003c40403c;
|
||||
case 'd': return 0x00020202023e423e;
|
||||
case 'e': return 0x0000003c427c403c;
|
||||
case 'f': return 0x00000c103c101010;
|
||||
case 'g': return 0x003e645840784438;
|
||||
case 'h': return 0x0020202038242424;
|
||||
case 'i': return 0x0000181800181818;
|
||||
case 'j': return 0x000606000606361c;
|
||||
case 'k': return 0x0020202c3830382c;
|
||||
case 'l': return 0x003010101010100c;
|
||||
case 'm': return 0x000000203e2a2a2a;
|
||||
case 'n': return 0x0000000038242424;
|
||||
case 'o': return 0x0000000018242418;
|
||||
case 'p': return 0x0000001c223c2020;
|
||||
case 'q': return 0x00000018241c0404;
|
||||
case 'r': return 0x0000002038242020;
|
||||
case 's': return 0x0000003840380438;
|
||||
case 't': return 0x0000103c1010100c;
|
||||
case 'u': return 0x000000002424241e;
|
||||
case 'v': return 0x0000002424243c18;
|
||||
case 'w': return 0x0000000063222a3e;
|
||||
case 'x': return 0x000000663c183c66;
|
||||
case 'y': return 0x00000024241c0438;
|
||||
case 'z': return 0x0000003e060c183e;
|
||||
case '{': return 0x000c18183018180c;
|
||||
case '|': return 0x1818181818181818;
|
||||
case '}': return 0x003018180c181830;
|
||||
case '~': return 0x0000307a5e0c0000;
|
||||
|
||||
default: return 0x55AA55AA55AA55AA;
|
||||
// clang-format on
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user