///|
/// Bitmap text rendering with a built-in 5x7 font (digits, uppercase
/// letters and basic punctuation). Glyphs are drawn through the clipped
/// drawing primitives, so text can safely overflow the canvas. Lowercase
/// letters render as uppercase; unknown characters render as a box.
///|
/// The 5x7 glyph as 7 rows of 5-bit masks (bit 4 = leftmost column).
fn glyph_rows(code : Int) -> Array[Int] {
// Map lowercase to uppercase.
let c = if code >= 97 && code <= 122 { code - 32 } else { code }
match c {
32 => [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00] // space
33 => [0x04, 0x04, 0x04, 0x04, 0x04, 0x00, 0x04] // !
45 => [0x00, 0x00, 0x00, 0x1F, 0x00, 0x00, 0x00] // -
46 => [0x00, 0x00, 0x00, 0x00, 0x00, 0x0C, 0x0C] // .
48 => [0x0E, 0x11, 0x13, 0x15, 0x19, 0x11, 0x0E] // 0
49 => [0x04, 0x0C, 0x04, 0x04, 0x04, 0x04, 0x0E] // 1
50 => [0x0E, 0x11, 0x01, 0x02, 0x04, 0x08, 0x1F] // 2
51 => [0x1F, 0x02, 0x04, 0x02, 0x01, 0x11, 0x0E] // 3
52 => [0x02, 0x06, 0x0A, 0x12, 0x1F, 0x02, 0x02] // 4
53 => [0x1F, 0x10, 0x1E, 0x01, 0x01, 0x11, 0x0E] // 5
54 => [0x06, 0x08, 0x10, 0x1E, 0x11, 0x11, 0x0E] // 6
55 => [0x1F, 0x01, 0x02, 0x04, 0x08, 0x08, 0x08] // 7
56 => [0x0E, 0x11, 0x11, 0x0E, 0x11, 0x11, 0x0E] // 8
57 => [0x0E, 0x11, 0x11, 0x0F, 0x01, 0x02, 0x0C] // 9
58 => [0x00, 0x0C, 0x0C, 0x00, 0x0C, 0x0C, 0x00] // :
63 => [0x0E, 0x11, 0x01, 0x02, 0x04, 0x00, 0x04] // ?
65 => [0x0E, 0x11, 0x11, 0x1F, 0x11, 0x11, 0x11] // A
66 => [0x1E, 0x11, 0x11, 0x1E, 0x11, 0x11, 0x1E] // B
67 => [0x0E, 0x11, 0x10, 0x10, 0x10, 0x11, 0x0E] // C
68 => [0x1C, 0x12, 0x11, 0x11, 0x11, 0x12, 0x1C] // D
69 => [0x1F, 0x10, 0x10, 0x1E, 0x10, 0x10, 0x1F] // E
70 => [0x1F, 0x10, 0x10, 0x1E, 0x10, 0x10, 0x10] // F
71 => [0x0E, 0x11, 0x10, 0x17, 0x11, 0x11, 0x0F] // G
72 => [0x11, 0x11, 0x11, 0x1F, 0x11, 0x11, 0x11] // H
73 => [0x0E, 0x04, 0x04, 0x04, 0x04, 0x04, 0x0E] // I
74 => [0x07, 0x02, 0x02, 0x02, 0x02, 0x12, 0x0C] // J
75 => [0x11, 0x12, 0x14, 0x18, 0x14, 0x12, 0x11] // K
76 => [0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x1F] // L
77 => [0x11, 0x1B, 0x15, 0x15, 0x11, 0x11, 0x11] // M
78 => [0x11, 0x11, 0x19, 0x15, 0x13, 0x11, 0x11] // N
79 => [0x0E, 0x11, 0x11, 0x11, 0x11, 0x11, 0x0E] // O
80 => [0x1E, 0x11, 0x11, 0x1E, 0x10, 0x10, 0x10] // P
81 => [0x0E, 0x11, 0x11, 0x11, 0x15, 0x12, 0x0D] // Q
82 => [0x1E, 0x11, 0x11, 0x1E, 0x14, 0x12, 0x11] // R
83 => [0x0F, 0x10, 0x10, 0x0E, 0x01, 0x01, 0x1E] // S
84 => [0x1F, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04] // T
85 => [0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x0E] // U
86 => [0x11, 0x11, 0x11, 0x11, 0x11, 0x0A, 0x04] // V
87 => [0x11, 0x11, 0x11, 0x15, 0x15, 0x15, 0x0A] // W
88 => [0x11, 0x11, 0x0A, 0x04, 0x0A, 0x11, 0x11] // X
89 => [0x11, 0x11, 0x11, 0x0A, 0x04, 0x04, 0x04] // Y
90 => [0x1F, 0x01, 0x02, 0x04, 0x08, 0x10, 0x1F] // Z
_ => [0x1F, 0x11, 0x11, 0x11, 0x11, 0x11, 0x1F] // unknown -> box
}
}
///|
/// Draws one character with its top-left corner at `(x, y)`, each font
/// pixel scaled to a `scale x scale` block (scale is clamped to >= 1).
pub fn Image::draw_char(
self : Image,
x : Int,
y : Int,
ch : Char,
scale : Int,
r : Byte,
g : Byte,
b : Byte,
a : Byte,
) -> Unit {
let s = if scale < 1 { 1 } else { scale }
let rows = glyph_rows(ch.to_int())
for row in 0..<7 {
for col in 0..<5 {
if rows[row] / (1 << (4 - col)) % 2 == 1 {
self.fill_rect(x + col * s, y + row * s, s, s, r, g, b, a)
}
}
}
}
///|
/// Draws a text string starting at `(x, y)`; the pen advances 6 font
/// columns (5 glyph + 1 spacing) per character. Returns nothing; text
/// overflowing the canvas is clipped, not wrapped.
pub fn Image::draw_text(
self : Image,
x : Int,
y : Int,
text : String,
scale : Int,
r : Byte,
g : Byte,
b : Byte,
a : Byte,
) -> Unit {
let s = if scale < 1 { 1 } else { scale }
let mut pen = x
for ch in text {
self.draw_char(pen, y, ch, s, r, g, b, a)
pen = pen + 6 * s
}
}