Skip to content

Instantly share code, notes, and snippets.

@paulsmith
Last active January 9, 2024 02:59
Show Gist options
  • Star 3 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save paulsmith/366eb9efc47cd2d27d5e0690058b22f2 to your computer and use it in GitHub Desktop.
Save paulsmith/366eb9efc47cd2d27d5e0690058b22f2 to your computer and use it in GitHub Desktop.
/**
* 8x8 monochrome bitmap fonts for rendering
* Author: Daniel Hepper <daniel@hepper.net>
*
* License: Public Domain
*
* Based on:
* // Summary: font8x8.h
* // 8x8 monochrome bitmap fonts for rendering
* //
* // Author:
* // Marcel Sondaar
* // International Business Machines (public domain VGA fonts)
* //
* // License:
* // Public Domain
*
* Fetched from: http://dimensionalrift.homelinux.net/combuster/mos3/?p=viewsource&file=/modules/gfx/font8_8.asm
**/
const font_8x8 = [
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // U+0000 (nul)
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // U+0001
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // U+0002
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // U+0003
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // U+0004
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // U+0005
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // U+0006
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // U+0007
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // U+0008
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // U+0009
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // U+000A
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // U+000B
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // U+000C
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // U+000D
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // U+000E
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // U+000F
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // U+0010
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // U+0011
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // U+0012
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // U+0013
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // U+0014
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // U+0015
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // U+0016
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // U+0017
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // U+0018
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // U+0019
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // U+001A
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // U+001B
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // U+001C
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // U+001D
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // U+001E
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // U+001F
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // U+0020 (space)
0x18, 0x3C, 0x3C, 0x18, 0x18, 0x00, 0x18, 0x00, // U+0021 (!)
0x36, 0x36, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // U+0022 (")
0x36, 0x36, 0x7F, 0x36, 0x7F, 0x36, 0x36, 0x00, // U+0023 (#)
0x0C, 0x3E, 0x03, 0x1E, 0x30, 0x1F, 0x0C, 0x00, // U+0024 ($)
0x00, 0x63, 0x33, 0x18, 0x0C, 0x66, 0x63, 0x00, // U+0025 (%)
0x1C, 0x36, 0x1C, 0x6E, 0x3B, 0x33, 0x6E, 0x00, // U+0026 (&)
0x06, 0x06, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, // U+0027 (')
0x18, 0x0C, 0x06, 0x06, 0x06, 0x0C, 0x18, 0x00, // U+0028 (()
0x06, 0x0C, 0x18, 0x18, 0x18, 0x0C, 0x06, 0x00, // U+0029 ())
0x00, 0x66, 0x3C, 0xFF, 0x3C, 0x66, 0x00, 0x00, // U+002A (*)
0x00, 0x0C, 0x0C, 0x3F, 0x0C, 0x0C, 0x00, 0x00, // U+002B (+)
0x00, 0x00, 0x00, 0x00, 0x00, 0x0C, 0x0C, 0x06, // U+002C (,)
0x00, 0x00, 0x00, 0x3F, 0x00, 0x00, 0x00, 0x00, // U+002D (-)
0x00, 0x00, 0x00, 0x00, 0x00, 0x0C, 0x0C, 0x00, // U+002E (.)
0x60, 0x30, 0x18, 0x0C, 0x06, 0x03, 0x01, 0x00, // U+002F (/)
0x3E, 0x63, 0x73, 0x7B, 0x6F, 0x67, 0x3E, 0x00, // U+0030 (0)
0x0C, 0x0E, 0x0C, 0x0C, 0x0C, 0x0C, 0x3F, 0x00, // U+0031 (1)
0x1E, 0x33, 0x30, 0x1C, 0x06, 0x33, 0x3F, 0x00, // U+0032 (2)
0x1E, 0x33, 0x30, 0x1C, 0x30, 0x33, 0x1E, 0x00, // U+0033 (3)
0x38, 0x3C, 0x36, 0x33, 0x7F, 0x30, 0x78, 0x00, // U+0034 (4)
0x3F, 0x03, 0x1F, 0x30, 0x30, 0x33, 0x1E, 0x00, // U+0035 (5)
0x1C, 0x06, 0x03, 0x1F, 0x33, 0x33, 0x1E, 0x00, // U+0036 (6)
0x3F, 0x33, 0x30, 0x18, 0x0C, 0x0C, 0x0C, 0x00, // U+0037 (7)
0x1E, 0x33, 0x33, 0x1E, 0x33, 0x33, 0x1E, 0x00, // U+0038 (8)
0x1E, 0x33, 0x33, 0x3E, 0x30, 0x18, 0x0E, 0x00, // U+0039 (9)
0x00, 0x0C, 0x0C, 0x00, 0x00, 0x0C, 0x0C, 0x00, // U+003A (:)
0x00, 0x0C, 0x0C, 0x00, 0x00, 0x0C, 0x0C, 0x06, // U+003B (;)
0x18, 0x0C, 0x06, 0x03, 0x06, 0x0C, 0x18, 0x00, // U+003C (<)
0x00, 0x00, 0x3F, 0x00, 0x00, 0x3F, 0x00, 0x00, // U+003D (=)
0x06, 0x0C, 0x18, 0x30, 0x18, 0x0C, 0x06, 0x00, // U+003E (>)
0x1E, 0x33, 0x30, 0x18, 0x0C, 0x00, 0x0C, 0x00, // U+003F (?)
0x3E, 0x63, 0x7B, 0x7B, 0x7B, 0x03, 0x1E, 0x00, // U+0040 (@)
0x0C, 0x1E, 0x33, 0x33, 0x3F, 0x33, 0x33, 0x00, // U+0041 (A)
0x3F, 0x66, 0x66, 0x3E, 0x66, 0x66, 0x3F, 0x00, // U+0042 (B)
0x3C, 0x66, 0x03, 0x03, 0x03, 0x66, 0x3C, 0x00, // U+0043 (C)
0x1F, 0x36, 0x66, 0x66, 0x66, 0x36, 0x1F, 0x00, // U+0044 (D)
0x7F, 0x46, 0x16, 0x1E, 0x16, 0x46, 0x7F, 0x00, // U+0045 (E)
0x7F, 0x46, 0x16, 0x1E, 0x16, 0x06, 0x0F, 0x00, // U+0046 (F)
0x3C, 0x66, 0x03, 0x03, 0x73, 0x66, 0x7C, 0x00, // U+0047 (G)
0x33, 0x33, 0x33, 0x3F, 0x33, 0x33, 0x33, 0x00, // U+0048 (H)
0x1E, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x1E, 0x00, // U+0049 (I)
0x78, 0x30, 0x30, 0x30, 0x33, 0x33, 0x1E, 0x00, // U+004A (J)
0x67, 0x66, 0x36, 0x1E, 0x36, 0x66, 0x67, 0x00, // U+004B (K)
0x0F, 0x06, 0x06, 0x06, 0x46, 0x66, 0x7F, 0x00, // U+004C (L)
0x63, 0x77, 0x7F, 0x7F, 0x6B, 0x63, 0x63, 0x00, // U+004D (M)
0x63, 0x67, 0x6F, 0x7B, 0x73, 0x63, 0x63, 0x00, // U+004E (N)
0x1C, 0x36, 0x63, 0x63, 0x63, 0x36, 0x1C, 0x00, // U+004F (O)
0x3F, 0x66, 0x66, 0x3E, 0x06, 0x06, 0x0F, 0x00, // U+0050 (P)
0x1E, 0x33, 0x33, 0x33, 0x3B, 0x1E, 0x38, 0x00, // U+0051 (Q)
0x3F, 0x66, 0x66, 0x3E, 0x36, 0x66, 0x67, 0x00, // U+0052 (R)
0x1E, 0x33, 0x07, 0x0E, 0x38, 0x33, 0x1E, 0x00, // U+0053 (S)
0x3F, 0x2D, 0x0C, 0x0C, 0x0C, 0x0C, 0x1E, 0x00, // U+0054 (T)
0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x3F, 0x00, // U+0055 (U)
0x33, 0x33, 0x33, 0x33, 0x33, 0x1E, 0x0C, 0x00, // U+0056 (V)
0x63, 0x63, 0x63, 0x6B, 0x7F, 0x77, 0x63, 0x00, // U+0057 (W)
0x63, 0x63, 0x36, 0x1C, 0x1C, 0x36, 0x63, 0x00, // U+0058 (X)
0x33, 0x33, 0x33, 0x1E, 0x0C, 0x0C, 0x1E, 0x00, // U+0059 (Y)
0x7F, 0x63, 0x31, 0x18, 0x4C, 0x66, 0x7F, 0x00, // U+005A (Z)
0x1E, 0x06, 0x06, 0x06, 0x06, 0x06, 0x1E, 0x00, // U+005B ([)
0x03, 0x06, 0x0C, 0x18, 0x30, 0x60, 0x40, 0x00, // U+005C (\)
0x1E, 0x18, 0x18, 0x18, 0x18, 0x18, 0x1E, 0x00, // U+005D (])
0x08, 0x1C, 0x36, 0x63, 0x00, 0x00, 0x00, 0x00, // U+005E (^)
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, // U+005F (_)
0x0C, 0x0C, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, // U+0060 (`)
0x00, 0x00, 0x1E, 0x30, 0x3E, 0x33, 0x6E, 0x00, // U+0061 (a)
0x07, 0x06, 0x06, 0x3E, 0x66, 0x66, 0x3B, 0x00, // U+0062 (b)
0x00, 0x00, 0x1E, 0x33, 0x03, 0x33, 0x1E, 0x00, // U+0063 (c)
0x38, 0x30, 0x30, 0x3e, 0x33, 0x33, 0x6E, 0x00, // U+0064 (d)
0x00, 0x00, 0x1E, 0x33, 0x3f, 0x03, 0x1E, 0x00, // U+0065 (e)
0x1C, 0x36, 0x06, 0x0f, 0x06, 0x06, 0x0F, 0x00, // U+0066 (f)
0x00, 0x00, 0x6E, 0x33, 0x33, 0x3E, 0x30, 0x1F, // U+0067 (g)
0x07, 0x06, 0x36, 0x6E, 0x66, 0x66, 0x67, 0x00, // U+0068 (h)
0x0C, 0x00, 0x0E, 0x0C, 0x0C, 0x0C, 0x1E, 0x00, // U+0069 (i)
0x30, 0x00, 0x30, 0x30, 0x30, 0x33, 0x33, 0x1E, // U+006A (j)
0x07, 0x06, 0x66, 0x36, 0x1E, 0x36, 0x67, 0x00, // U+006B (k)
0x0E, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x1E, 0x00, // U+006C (l)
0x00, 0x00, 0x33, 0x7F, 0x7F, 0x6B, 0x63, 0x00, // U+006D (m)
0x00, 0x00, 0x1F, 0x33, 0x33, 0x33, 0x33, 0x00, // U+006E (n)
0x00, 0x00, 0x1E, 0x33, 0x33, 0x33, 0x1E, 0x00, // U+006F (o)
0x00, 0x00, 0x3B, 0x66, 0x66, 0x3E, 0x06, 0x0F, // U+0070 (p)
0x00, 0x00, 0x6E, 0x33, 0x33, 0x3E, 0x30, 0x78, // U+0071 (q)
0x00, 0x00, 0x3B, 0x6E, 0x66, 0x06, 0x0F, 0x00, // U+0072 (r)
0x00, 0x00, 0x3E, 0x03, 0x1E, 0x30, 0x1F, 0x00, // U+0073 (s)
0x08, 0x0C, 0x3E, 0x0C, 0x0C, 0x2C, 0x18, 0x00, // U+0074 (t)
0x00, 0x00, 0x33, 0x33, 0x33, 0x33, 0x6E, 0x00, // U+0075 (u)
0x00, 0x00, 0x33, 0x33, 0x33, 0x1E, 0x0C, 0x00, // U+0076 (v)
0x00, 0x00, 0x63, 0x6B, 0x7F, 0x7F, 0x36, 0x00, // U+0077 (w)
0x00, 0x00, 0x63, 0x36, 0x1C, 0x36, 0x63, 0x00, // U+0078 (x)
0x00, 0x00, 0x33, 0x33, 0x33, 0x3E, 0x30, 0x1F, // U+0079 (y)
0x00, 0x00, 0x3F, 0x19, 0x0C, 0x26, 0x3F, 0x00, // U+007A (z)
0x38, 0x0C, 0x0C, 0x07, 0x0C, 0x0C, 0x38, 0x00, // U+007B ({)
0x18, 0x18, 0x18, 0x00, 0x18, 0x18, 0x18, 0x00, // U+007C (|)
0x07, 0x0C, 0x0C, 0x38, 0x0C, 0x0C, 0x07, 0x00, // U+007D (})
0x6E, 0x3B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // U+007E (~)
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // U+007F
];
<!DOCTYPE html>
<html>
<head>
<title>Old school screen graphics and text</title>
<style>
body {
display: flex;
align-items: center;
height: 100vh;
justify-content: center;
}
canvas {
border: 1px solid black;
}
</style>
</head>
<body>
<main>
<canvas width="320" height="200"></canvas>
</main>
<script src="./font_8x8.js"></script>
<script>
const COLUMNS = 40;
const ROWS = 25;
const canvas = document.querySelector('canvas');
const ctx = canvas.getContext('2d');
const FONT_WIDTH = canvas.width / COLUMNS;
const FONT_HEIGHT = canvas.height / ROWS;
const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
const screenMem = imageData.data;
const pixelsPerColumn = canvas.width / COLUMNS;
const pixelsPerRow = canvas.height / ROWS;
// color is a RGBA 32-bit number
function clearScreen(color) {
for (let i = 0; i < screenMem.length; i += 4) {
screenMem[i + 0] = (color >> 24) & 0xff;
screenMem[i + 1] = (color >> 16) & 0xff;
screenMem[i + 2] = (color >> 8) & 0xff;
screenMem[i + 3] = (color >> 0) & 0xff;
}
ctx.putImageData(imageData, 0, 0);
}
function drawPixel(x, y, color) {
const index = (y * canvas.width + x) * 4;
screenMem[index + 0] = (color >> 24) & 0xff;
screenMem[index + 1] = (color >> 16) & 0xff;
screenMem[index + 2] = (color >> 8) & 0xff;
screenMem[index + 3] = (color >> 0) & 0xff;
ctx.putImageData(imageData, 0, 0);
}
// x and y are column and row, not in terms of pixels
function drawChar(x, y, ch, color) {
const cp = ch.charCodeAt(0);
const bitmap = font_8x8.slice(cp * FONT_WIDTH, cp * FONT_WIDTH + FONT_WIDTH);
for (let cy = 0; cy < FONT_HEIGHT; cy++) {
let bmRow = bitmap[cy];
for (let cx = 0; cx < FONT_WIDTH; cx++) {
if ((bmRow & (1 << cx)) > 0) {
drawPixel(x * pixelsPerColumn + cx, y * pixelsPerRow + cy, color);
}
}
}
}
// x and y are column and row, not in terms of pixels
function drawString(x, y, s, color) {
let col = x;
let row = y;
for (let i = 0; i < s.length; i++) {
let ch = s[i];
if (ch === "\n") {
row += 1;
col = 0;
} else {
drawChar(col, row, ch, color);
col += 1;
if (col >= COLUMNS) {
row += 1;
col = 0;
}
}
}
return [col, row];
}
function drawLine(x0, y0, x1, y1, color) {
let dx = Math.abs(x1 - x0);
let dy = Math.abs(y1 - y0);
let sx = (x0 < x1) ? 1 : -1;
let sy = (y0 < y1) ? 1 : -1;
let err = dx - dy;
while (true) {
drawPixel(x0, y0, color);
if (x0 === x1 && y0 === y1) break;
let e2 = 2 * err;
if (e2 > -dy) {
err -= dy;
x0 += sx;
}
if (e2 < dx) {
err += dx;
y0 += sy;
}
}
}
function rgba(r, g, b, a) {
return (r << 24) | (g << 16) | (b << 8) | a;
}
clearScreen(0x867ADEFF); // C64 bg
drawChar(0, 0, 'A', 0xff0000ff);
drawChar(1, 0, 'B', 0x0000ffff);
drawChar(0, 1, 'C', 0x00ff00ff);
drawString(0, 2, "Hello, world!", 0xffffffff);
for (let i = 0; i < 10; i++) {
drawLine(0, 100, 319, i * 15 + 50, rgba(0xff, 0xff, 25 * i, 0xff));
}
</script>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment