Harbor

branch main
showing the latest snapshot on main
base_font.odin 6.8 KB · Plain text
gpu/renderer/base_font.odin 0644 Raw
package renderer

BASE_FONT_WIDTH        :: 5
BASE_FONT_HEIGHT       :: 7
BASE_FONT_ADVANCE      :: 6
BASE_FONT_LINE_ADVANCE :: 8

@(private)
base_font_normalize_char :: proc(ch: u8) -> u8 {
	if ch >= 'a' && ch <= 'z' {
		return ch - ('a' - 'A')
	}
	return ch
}

@(private)
base_font_unknown_glyph :: proc() -> [BASE_FONT_HEIGHT]u8 {
	return {
		0b11111,
		0b10001,
		0b10101,
		0b10001,
		0b10101,
		0b10001,
		0b11111,
	}
}

base_font_glyph :: proc(ch: u8) -> [BASE_FONT_HEIGHT]u8 {
	switch base_font_normalize_char(ch) {
	case '0': return {0b01110, 0b10001, 0b10011, 0b10101, 0b11001, 0b10001, 0b01110}
	case '1': return {0b00100, 0b01100, 0b00100, 0b00100, 0b00100, 0b00100, 0b01110}
	case '2': return {0b01110, 0b10001, 0b00001, 0b00010, 0b00100, 0b01000, 0b11111}
	case '3': return {0b11110, 0b00001, 0b00001, 0b01110, 0b00001, 0b00001, 0b11110}
	case '4': return {0b00010, 0b00110, 0b01010, 0b10010, 0b11111, 0b00010, 0b00010}
	case '5': return {0b11111, 0b10000, 0b10000, 0b11110, 0b00001, 0b00001, 0b11110}
	case '6': return {0b01110, 0b10000, 0b10000, 0b11110, 0b10001, 0b10001, 0b01110}
	case '7': return {0b11111, 0b00001, 0b00010, 0b00100, 0b01000, 0b01000, 0b01000}
	case '8': return {0b01110, 0b10001, 0b10001, 0b01110, 0b10001, 0b10001, 0b01110}
	case '9': return {0b01110, 0b10001, 0b10001, 0b01111, 0b00001, 0b00001, 0b01110}

	case 'A': return {0b01110, 0b10001, 0b10001, 0b11111, 0b10001, 0b10001, 0b10001}
	case 'B': return {0b11110, 0b10001, 0b10001, 0b11110, 0b10001, 0b10001, 0b11110}
	case 'C': return {0b01111, 0b10000, 0b10000, 0b10000, 0b10000, 0b10000, 0b01111}
	case 'D': return {0b11110, 0b10001, 0b10001, 0b10001, 0b10001, 0b10001, 0b11110}
	case 'E': return {0b11111, 0b10000, 0b10000, 0b11110, 0b10000, 0b10000, 0b11111}
	case 'F': return {0b11111, 0b10000, 0b10000, 0b11110, 0b10000, 0b10000, 0b10000}
	case 'G': return {0b01111, 0b10000, 0b10000, 0b10111, 0b10001, 0b10001, 0b01111}
	case 'H': return {0b10001, 0b10001, 0b10001, 0b11111, 0b10001, 0b10001, 0b10001}
	case 'I': return {0b11111, 0b00100, 0b00100, 0b00100, 0b00100, 0b00100, 0b11111}
	case 'J': return {0b00001, 0b00001, 0b00001, 0b00001, 0b10001, 0b10001, 0b01110}
	case 'K': return {0b10001, 0b10010, 0b10100, 0b11000, 0b10100, 0b10010, 0b10001}
	case 'L': return {0b10000, 0b10000, 0b10000, 0b10000, 0b10000, 0b10000, 0b11111}
	case 'M': return {0b10001, 0b11011, 0b10101, 0b10101, 0b10001, 0b10001, 0b10001}
	case 'N': return {0b10001, 0b11001, 0b10101, 0b10011, 0b10001, 0b10001, 0b10001}
	case 'O': return {0b01110, 0b10001, 0b10001, 0b10001, 0b10001, 0b10001, 0b01110}
	case 'P': return {0b11110, 0b10001, 0b10001, 0b11110, 0b10000, 0b10000, 0b10000}
	case 'Q': return {0b01110, 0b10001, 0b10001, 0b10001, 0b10101, 0b10010, 0b01101}
	case 'R': return {0b11110, 0b10001, 0b10001, 0b11110, 0b10100, 0b10010, 0b10001}
	case 'S': return {0b01111, 0b10000, 0b10000, 0b01110, 0b00001, 0b00001, 0b11110}
	case 'T': return {0b11111, 0b00100, 0b00100, 0b00100, 0b00100, 0b00100, 0b00100}
	case 'U': return {0b10001, 0b10001, 0b10001, 0b10001, 0b10001, 0b10001, 0b01110}
	case 'V': return {0b10001, 0b10001, 0b10001, 0b10001, 0b10001, 0b01010, 0b00100}
	case 'W': return {0b10001, 0b10001, 0b10001, 0b10101, 0b10101, 0b10101, 0b01010}
	case 'X': return {0b10001, 0b10001, 0b01010, 0b00100, 0b01010, 0b10001, 0b10001}
	case 'Y': return {0b10001, 0b10001, 0b01010, 0b00100, 0b00100, 0b00100, 0b00100}
	case 'Z': return {0b11111, 0b00001, 0b00010, 0b00100, 0b01000, 0b10000, 0b11111}

	case ':': return {0b00000, 0b00100, 0b00100, 0b00000, 0b00100, 0b00100, 0b00000}
	case '.': return {0b00000, 0b00000, 0b00000, 0b00000, 0b00000, 0b01100, 0b01100}
	case ',': return {0b00000, 0b00000, 0b00000, 0b00000, 0b00100, 0b00100, 0b01000}
	case '-': return {0b00000, 0b00000, 0b00000, 0b11111, 0b00000, 0b00000, 0b00000}
	case '_': return {0b00000, 0b00000, 0b00000, 0b00000, 0b00000, 0b00000, 0b11111}
	case '+': return {0b00000, 0b00100, 0b00100, 0b11111, 0b00100, 0b00100, 0b00000}
	case '=': return {0b00000, 0b00000, 0b11111, 0b00000, 0b11111, 0b00000, 0b00000}
	case '!': return {0b00100, 0b00100, 0b00100, 0b00100, 0b00100, 0b00000, 0b00100}
	case '?': return {0b01110, 0b10001, 0b00001, 0b00010, 0b00100, 0b00000, 0b00100}
	case '/': return {0b00001, 0b00010, 0b00100, 0b00100, 0b01000, 0b10000, 0b10000}
	case '\\': return {0b10000, 0b01000, 0b00100, 0b00100, 0b00010, 0b00001, 0b00001}
	case '(': return {0b00010, 0b00100, 0b01000, 0b01000, 0b01000, 0b00100, 0b00010}
	case ')': return {0b01000, 0b00100, 0b00010, 0b00010, 0b00010, 0b00100, 0b01000}
	case '[': return {0b01110, 0b01000, 0b01000, 0b01000, 0b01000, 0b01000, 0b01110}
	case ']': return {0b01110, 0b00010, 0b00010, 0b00010, 0b00010, 0b00010, 0b01110}
	case '<': return {0b00010, 0b00100, 0b01000, 0b10000, 0b01000, 0b00100, 0b00010}
	case '>': return {0b01000, 0b00100, 0b00010, 0b00001, 0b00010, 0b00100, 0b01000}
	case '#': return {0b01010, 0b01010, 0b11111, 0b01010, 0b11111, 0b01010, 0b01010}
	case '%': return {0b11001, 0b11010, 0b00100, 0b01000, 0b10110, 0b00110, 0b00000}
	case '\'': return {0b00100, 0b00100, 0b01000, 0b00000, 0b00000, 0b00000, 0b00000}
	case '"': return {0b01010, 0b01010, 0b01010, 0b00000, 0b00000, 0b00000, 0b00000}
	}
	return base_font_unknown_glyph()
}

base_font_add_text :: proc(batch: ^Batch_2D, x, y: f32, text: string, color: [4]f32, scale: f32 = 1.0) {
	if scale <= 0 {
		return
	}

	cursor_x := x
	cursor_y := y
	for i in 0..<len(text) {
		ch := text[i]
		if ch == '\n' {
			cursor_x = x
			cursor_y += f32(BASE_FONT_LINE_ADVANCE) * scale
			continue
		}
		if ch == ' ' || ch == '\t' {
			cursor_x += f32(BASE_FONT_ADVANCE) * scale
			continue
		}

		rows := base_font_glyph(ch)
		for row in 0..<BASE_FONT_HEIGHT {
			bits := rows[row]
			col := 0
			for col < BASE_FONT_WIDTH {
				mask := u8(1 << u32(BASE_FONT_WIDTH - 1 - col))
				if (bits & mask) == 0 {
					col += 1
					continue
				}

				start_col := col
				for col < BASE_FONT_WIDTH {
					run_mask := u8(1 << u32(BASE_FONT_WIDTH - 1 - col))
					if (bits & run_mask) == 0 {
						break
					}
					col += 1
				}

				batch_add_rectangle(
					batch,
					cursor_x + f32(start_col) * scale,
					cursor_y + f32(row) * scale,
					f32(col - start_col) * scale,
					scale,
					color,
				)
			}
		}
		cursor_x += f32(BASE_FONT_ADVANCE) * scale
	}
}

base_font_text_width :: proc(text: string, scale: f32 = 1.0) -> f32 {
	if scale <= 0 || len(text) == 0 {
		return 0
	}

	max_chars := 0
	line_chars := 0
	for i in 0..<len(text) {
		if text[i] == '\n' {
			max_chars = max(max_chars, line_chars)
			line_chars = 0
			continue
		}
		line_chars += 1
	}
	max_chars = max(max_chars, line_chars)
	return f32(max_chars * BASE_FONT_ADVANCE) * scale
}

base_font_text_height :: proc(text: string, scale: f32 = 1.0) -> f32 {
	if scale <= 0 || len(text) == 0 {
		return 0
	}

	lines := 1
	for i in 0..<len(text) {
		if text[i] == '\n' && i + 1 < len(text) {
			lines += 1
		}
	}
	return f32(lines * BASE_FONT_LINE_ADVANCE) * scale
}