diff options
author | Mitya Selivanov <automainint@guattari.tech> | 2025-04-25 13:04:09 +0200 |
---|---|---|
committer | Mitya Selivanov <automainint@guattari.tech> | 2025-04-25 13:04:09 +0200 |
commit | 32f60821408fe51d0acf45e430556c12711e5cc5 (patch) | |
tree | 6d369d3456b077666cf3049ae0d528cd7300cfb7 | |
parent | 9251ce60ba4f68e813bd2c6fd06f7574e8b1f448 (diff) | |
download | reduced_system_layer-32f60821408fe51d0acf45e430556c12711e5cc5.zip |
Rework LCD font
-rw-r--r-- | graphics.c | 326 |
1 files changed, 227 insertions, 99 deletions
@@ -245,17 +245,6 @@ void clean_graphics_requests_cache(i64 amount); #ifndef GRAPHICS_IMPL_GUARD_ #define GRAPHICS_IMPL_GUARD_ -// FIXME, PERF: Re-encode for faster rendering. -static u64 _bitfont[] = { - 0xbc0000000000, 0xc00300000, 0x5fd5040093f24fc9, 0xa00a2c2a1a280105, 0xc000415e6f, 0x400000020be0000, 0x1c38a8400000007d, 0x40002043e1020215, 0x408102000000010, 0x9800000000020002, 0xf913e00000033, 0x53200000207c8800, 0x3654880000099, 0x54b800000f840e00, 0xe953c000001a, 0x953e000000674080, 0x1e54b800000f, 0x490000000000240, 0x88a08000000, 0x20a220050a142850, 0x6520800000, 0x912f801eab260be, 0x800034952bf0001f, 0xc850bf0000921427, 0xf00010a54afc0003, 0xd29427800002142b, 0x840007e1023f0000, 0x7d09100000217e, 0x3f000188a08fc000, 0xc30c0cfc00000810, 0x27803f101013f00f, 0xc244bf0000f214, 0x4bf0002f21427800, 0xc254a480006c24, 0x407c00102fc08100, 0xf208080f0000fa0, 0x531007d81c607c0, 0xc208288c031141, 0x83fc00046954b10, 0x180e03000000, 0x41040000000ff04, 0x8102040810000404, 0x2a54600000000101, 0x309123e0000e, 0xc912180000a22447, 0x8000062a54700007, 0xe52a4300000029f0, 0xa0000602043e0001, 0x1d48000002074, 0x1f000003610f8000, 0x13e04f800000010, 0x470000780813e00f, 0x184893e0000e224, 0x23e0001f12243000, 0x82a54100000008, 0x40780000009f0200, 0xe208080e0001f20, 0xa22007981860780, 0x82082888022282, 0x16c200004ca95320, 0x7f000004, 0x408200000086d04, 0x8204, -}; - -enum { - CHAR_NUM_BITS_X_ = 6, - CHAR_NUM_BITS_Y_ = 7, - CHAR_NUM_BITS_ = CHAR_NUM_BITS_X_ * CHAR_NUM_BITS_Y_, -}; - static void put_pixel_(vec4_f32 *dst, vec4_f32 color) { if (color.w == 1.f) *dst = color; @@ -557,6 +546,8 @@ static b8 quad_from_line_(vec2 *dst, vec2 vertices[2], f64 width) { } void fill_line_to_buffer(Pixel_Buffer dst, vec4_f32 color, vec2 vertices[2], f64 width) { + // FIXME, PERF: Use Bresenham's algorithm when width is 1. + if (width < EPSILON) return; PROFILER_begin(PROFILE_FILL_LINE); @@ -568,75 +559,174 @@ void fill_line_to_buffer(Pixel_Buffer dst, vec4_f32 color, vec2 vertices[2], f64 PROFILER_end(PROFILE_FILL_LINE); } -static i64 char_column_offset_(c32 c, i64 column_index) { - if (column_index < 0 || column_index >= CHAR_NUM_BITS_X_) - return -1; - return (c - 32) * CHAR_NUM_BITS_ + column_index * CHAR_NUM_BITS_Y_; -} +// ================================================================ +// +// LCD text rendering +// +// ================================================================ -static b8 char_bit_(i64 column_offset, i64 row_index) { - if (column_offset < 0 || row_index < 0 || row_index >= CHAR_NUM_BITS_Y_) - return 0; +static u64 _lcd_bits[] = { + 0x10001010101ull, + 0x505ull, + 0xa1f0a0a1f0a00ull, + 0x40e140e050e04ull, + 0x814081f020502ull, + 0x7090503060506ull, + 0x101ull, + 0x2010101010102ull, + 0x1020202020201ull, + 0x120c3f0c12ull, + 0x4041f040400ull, + 0x1020000000000ull, + 0x1f000000ull, + 0x10000000000ull, + 0x1010202020404ull, + 0x70505050700ull, + 0x70202020300ull, + 0x70102040700ull, + 0x70402040700ull, + 0x40407050500ull, + 0x30407010700ull, + 0x70507010600ull, + 0x20202040700ull, + 0x70507050700ull, + 0x30407050700ull, + 0x10000010000ull, + 0x1020000020000ull, + 0x40201020400ull, + 0x1f001f0000ull, + 0x10204020100ull, + 0x20002040502ull, + 0xe01191519110eull, + 0x9090f090906ull, + 0x70909070907ull, + 0x60901010906ull, + 0x70909090907ull, + 0xf010107010full, + 0x1010107010full, + 0x6090d010906ull, + 0x909090f0909ull, + 0x70202020207ull, + 0x20504040407ull, + 0x90905030509ull, + 0x70101010101ull, + 0x1111151f1b11ull, + 0x111119151311ull, + 0x60909090906ull, + 0x10107090907ull, + 0x8060909090906ull, + 0x90907090907ull, + 0x60908060106ull, + 0x4040404041full, + 0x60909090909ull, + 0x40a11111111ull, + 0xa1f15151111ull, + 0x11110a040a11ull, + 0x102040a1111ull, + 0xf010204080full, + 0x3010101010103ull, + 0x4040202020101ull, + 0x3020202020203ull, + 0x110a04ull, + 0x3f000000000000ull, + 0x201ull, + 0x6090f080600ull, + 0x70909070100ull, + 0x60901090600ull, + 0xe09090e0800ull, + 0x6010f090600ull, + 0x20207020600ull, + 0x6080e09090600ull, + 0x90907010100ull, + 0x70203000200ull, + 0x1020203000200ull, + 0x50503050500ull, + 0x20101010101ull, + 0x151515150f00ull, + 0x90909090700ull, + 0x60909090600ull, + 0x10709090700ull, + 0x80e09090e00ull, + 0x10101030500ull, + 0x60806010600ull, + 0x20202020700ull, + 0xe0909090900ull, + 0x40a11111100ull, + 0xa1f15111100ull, + 0x110a040a1100ull, + 0x102040a1100ull, + 0xf0106080f00ull, + 0x4020201020204ull, + 0x1010101010101ull, + 0x1020204020201ull, + 0x1926ull, +}; + +enum { + LCD_Width = 6, + LCD_Height = 7, + LCD_Num_Bits = LCD_Width * LCD_Height, + LCD_First = 33, + LCD_Space = 4, + LCD_Len = sizeof(_lcd_bits) / sizeof(*_lcd_bits), +}; - i64 bit_index = column_offset + row_index; - i64 qword_index = bit_index / 64; - if (qword_index < 0 || qword_index >= (i64) (sizeof _bitfont / sizeof *_bitfont)) +static u64 lcd_column_convolved_(c32 c, i64 column_index) { + if (column_index < 0 || column_index >= LCD_Width) return 0; - u64 mask = 1ull << (bit_index % 64); - return !!(_bitfont[qword_index] & mask); -} -static u64 char_column_convolved_(c32 c, i64 column_index) { - if (column_index < 0 || column_index >= CHAR_NUM_BITS_X_) + i64 index = c - LCD_First; + if (index < 0 || index >= LCD_Len) return 0; u64 column = 0; - i64 offset = char_column_offset_(c, column_index); - - for (i64 y = 0; y < CHAR_NUM_BITS_Y_; ++y) - if (char_bit_(offset, y)) + for (i64 y = 0; y < LCD_Height; ++y) + if ((_lcd_bits[index] & (1ull << (y * 8 + column_index))) != 0) column |= 3ull << y; - return column; } -static b8 char_column_empty_(c32 c, i64 column_index) { - if (column_index < 0 || column_index >= CHAR_NUM_BITS_X_) +static b8 lcd_column_empty_(c32 c, i64 column_index) { + if (column_index < 0 || column_index >= LCD_Width) return 1; - i64 offset = char_column_offset_(c, column_index); + i64 index = c - LCD_First; + if (index < 0 || index >= LCD_Len) + return 1; - for (i64 y = 0; y < CHAR_NUM_BITS_Y_; ++y) - if (char_bit_(offset, y)) + for (i64 y = 0; y < LCD_Height; ++y) + if ((_lcd_bits[index] & (1ull << (y * 8 + column_index))) != 0) return 0; - return 1; } -static i64 char_width_(c32 c) { - if (c < 32) +static i64 lcd_char_width_(c32 c) { + // FIXME, PERF: Pre-calculate dimensions. + + i64 index = c - LCD_First; + if (index < -1) return 0; - if (c == ' ' || c > 127) - return 4; + if (index < 0 || index >= LCD_Len) + return LCD_Space; i64 width = 0; - - for (; width < CHAR_NUM_BITS_X_; ++width) - if (char_column_empty_(c, width) && char_column_empty_(c, width + 1)) + for (; width < LCD_Width; ++width) + if (lcd_column_empty_(c, width) && lcd_column_empty_(c, width + 1)) break; - return width; } -static i64 char_spacing_(i64 num_chars, c32 *text, i64 index) { +static i64 lcd_spacing_(i64 num_chars, c32 *text, i64 index) { + // FIXME, PERF: Pre-calculate kerning. + if (text == NULL) return 0; if (index < 0 || index + 1 >= num_chars) return 0; - u64 a = char_column_convolved_(text[index], char_width_(text[index]) - 1); - u64 b = char_column_convolved_(text[index + 1], 0); + u64 a = lcd_column_convolved_(text[index], lcd_char_width_(text[index]) - 1); + u64 b = lcd_column_convolved_(text[index + 1], 0); if (!!(a & b)) return 1; @@ -644,7 +734,7 @@ static i64 char_spacing_(i64 num_chars, c32 *text, i64 index) { return 0; } -static i64 text_cursor_(i64 num_chars, c32 *text) { +static i64 lcd_cursor_(i64 num_chars, c32 *text) { if (text == NULL) return 0; @@ -655,20 +745,20 @@ static i64 text_cursor_(i64 num_chars, c32 *text) { if (text[i] == '\n') cursor = 0; else if (text[i] == '\b' && i > 0) - cursor -= char_width_(text[i - 1]) + char_spacing_(num_chars, text, i - 1); + cursor -= lcd_char_width_(text[i - 1]) + lcd_spacing_(num_chars, text, i - 1); else if (text[i] == '\r') cursor = 0; else - cursor += char_width_(' ') + char_spacing_(num_chars, text, i); + cursor += lcd_char_width_(' ') + lcd_spacing_(num_chars, text, i); continue; } - cursor += char_width_(text[i]) + char_spacing_(num_chars, text, i); + cursor += lcd_char_width_(text[i]) + lcd_spacing_(num_chars, text, i); } return cursor; } -static i64 enum_text_columns_(i64 num_chars, c32 *text) { +static i64 lcd_enum_text_columns_(i64 num_chars, c32 *text) { if (text == NULL) return 0; @@ -684,16 +774,16 @@ static i64 enum_text_columns_(i64 num_chars, c32 *text) { } else if (text[i] == '\b' && i > 0) { if (cols < n) cols = n; - n -= char_width_(text[i - 1]) + char_spacing_(num_chars, text, i - 1); + n -= lcd_char_width_(text[i - 1]) + lcd_spacing_(num_chars, text, i - 1); } else if (text[i] == '\r') { if (cols < n) cols = n; n = 0; } else - n += char_width_(' ') + char_spacing_(num_chars, text, i); + n += lcd_char_width_(' ') + lcd_spacing_(num_chars, text, i); continue; } - n += char_width_(text[i]) + char_spacing_(num_chars, text, i); + n += lcd_char_width_(text[i]) + lcd_spacing_(num_chars, text, i); } if (cols < n) @@ -702,7 +792,7 @@ static i64 enum_text_columns_(i64 num_chars, c32 *text) { return cols; } -static i64 enum_text_rows_(i64 num_chars, c32 *text) { +static i64 lcd_enum_text_rows_(i64 num_chars, c32 *text) { if (text == NULL) return 0; @@ -712,15 +802,15 @@ static i64 enum_text_rows_(i64 num_chars, c32 *text) { if (i == num_chars || text[i] == '\n') { if (rows > 0) ++rows; - rows += CHAR_NUM_BITS_Y_; + rows += LCD_Height; } return rows; } -static void draw_text_(Pixel_Buffer dst, vec4_f32 color, f64 x0, f64 y0, f64 scale_x, f64 scale_y, i64 num_chars, c32 *text) { +static void lcd_draw_text_(Pixel_Buffer dst, vec4_f32 color, f64 x0, f64 y0, f64 scale_x, f64 scale_y, i64 num_chars, c32 *text) { f64 kx = scale_x; - f64 h = scale_y * CHAR_NUM_BITS_Y_; + f64 h = scale_y * LCD_Height; if (h < EPSILON) return; @@ -732,25 +822,26 @@ static void draw_text_(Pixel_Buffer dst, vec4_f32 color, f64 x0, f64 y0, f64 sca if (text[n] <= ' ') { if (text[n] == '\n') { x = x0; - y += scale_y * (CHAR_NUM_BITS_Y_ + 1); + y += scale_y * (LCD_Height + 1); } else if (text[n] == '\b' && n > 0) - x -= kx * (char_width_(text[n - 1]) + char_spacing_(num_chars, text, n - 1)); + x -= kx * (lcd_char_width_(text[n - 1]) + lcd_spacing_(num_chars, text, n - 1)); else if (text[n] == '\r') x = x0; else - x += kx * (char_width_(' ') + char_spacing_(num_chars, text, n)); + x += kx * (lcd_char_width_(' ') + lcd_spacing_(num_chars, text, n)); continue; } - i64 num_cols = char_width_(text[n]); + i64 num_cols = lcd_char_width_(text[n]); f64 w = num_cols * kx; + i64 index = text[n] - LCD_First; - if (w >= EPSILON) { - i64 i0 = (i64) floor(x); - i64 i1 = (i64) ceil (x + w); - i64 j0 = (i64) floor(y); - i64 j1 = (i64) ceil (y + h); + if (w >= EPSILON && index >= 0 && index < LCD_Len) { + i64 i0 = (i64) floor(x + 0.5); + i64 i1 = (i64) floor(x + w - 0.5); + i64 j0 = (i64) floor(y + 0.5); + i64 j1 = (i64) floor(y + h - 0.5); if (i0 < 0) i0 = 0; if (i1 >= dst.width) i1 = dst.width - 1; @@ -760,19 +851,20 @@ static void draw_text_(Pixel_Buffer dst, vec4_f32 color, f64 x0, f64 y0, f64 sca f64 w_inv = 1. / w; f64 h_inv = 1. / h; - for (i64 i = i0; i <= i1; ++i) { - i64 column = (i64) floor(((i - x + .5) * num_cols) * w_inv); - i64 offset = char_column_offset_(text[n], column); + // FIXME, PERF: Use integer arithmetic. + + for (i64 j = j0; j <= j1; ++j) { + i64 row = (i64) floor(((j - y + 0.5) * LCD_Height) * h_inv); + for (i64 i = i0; i <= i1; ++i) { + i64 column = (i64) floor(((i - x + 0.5) * num_cols) * w_inv); - for (i64 j = j0; j <= j1; ++j) { - i64 row = (i64) floor(((j - y + .5) * CHAR_NUM_BITS_Y_) * h_inv); - if (char_bit_(offset, row)) + if ((_lcd_bits[index] & (1ull << (row * 8 + column))) != 0) put_pixel_(dst.pixels + j * dst.stride + i, color); } } } - x += kx * (num_cols + char_spacing_(num_chars, text, n)); + x += kx * (num_cols + lcd_spacing_(num_chars, text, n)); } } @@ -808,8 +900,8 @@ void draw_text_area_to_buffer(Pixel_Buffer dst, i32 font, vec4_f32 color, Box ar PROFILER_begin(PROFILE_DRAW_TEXT_AREA); - i64 num_columns = enum_text_columns_(num_chars, text); - i64 num_rows = enum_text_rows_(num_chars, text); + i64 num_columns = lcd_enum_text_columns_(num_chars, text); + i64 num_rows = lcd_enum_text_rows_(num_chars, text); f64 scale_x = area.width / num_columns; f64 scale_y = area.height / num_rows; @@ -822,7 +914,7 @@ void draw_text_area_to_buffer(Pixel_Buffer dst, i32 font, vec4_f32 color, Box ar kx = k * max_size.x; ky = k * max_size.y; - draw_text_(dst, color, area.x, area.y, kx, ky, num_chars, text); + lcd_draw_text_(dst, color, area.x, area.y, kx, ky, num_chars, text); PROFILER_end(PROFILE_DRAW_TEXT_AREA); } @@ -835,10 +927,10 @@ void draw_text_cursor_to_buffer(Pixel_Buffer dst, i32 font, vec4_f32 color, Box PROFILER_begin(PROFILE_DRAW_TEXT_CURSOR); - i64 num_columns = enum_text_columns_(num_chars, text); - i64 num_rows = enum_text_rows_(num_chars, text); - i64 cursor_x = text_cursor_(cursor, text); - i64 cursor_y = enum_text_rows_(cursor, text); + i64 num_columns = lcd_enum_text_columns_(num_chars, text); + i64 num_rows = lcd_enum_text_rows_(num_chars, text); + i64 cursor_x = lcd_cursor_(cursor, text); + i64 cursor_y = lcd_enum_text_rows_(cursor, text); f64 scale_x = area.width / num_columns; f64 scale_y = area.height / num_rows; @@ -855,13 +947,13 @@ void draw_text_cursor_to_buffer(Pixel_Buffer dst, i32 font, vec4_f32 color, Box i64 selection_x, selection_y; if (selection > 0) { - selection_x = text_cursor_(cursor + selection, text); - selection_y = enum_text_rows_(cursor + selection, text); + selection_x = lcd_cursor_(cursor + selection, text); + selection_y = lcd_enum_text_rows_(cursor + selection, text); } else { selection_x = cursor_x; selection_y = cursor_y; - cursor_x = text_cursor_(cursor + selection, text); - cursor_y = enum_text_rows_(cursor + selection, text); + cursor_x = lcd_cursor_(cursor + selection, text); + cursor_y = lcd_enum_text_rows_(cursor + selection, text); } if (cursor_y == selection_y) @@ -870,9 +962,9 @@ void draw_text_cursor_to_buffer(Pixel_Buffer dst, i32 font, vec4_f32 color, Box color, (Box) { .x = area.x + kx * cursor_x, - .y = area.y + ky * cursor_y - ky * (CHAR_NUM_BITS_Y_ + 1), + .y = area.y + ky * cursor_y - ky * (LCD_Height + 1), .width = kx * (selection_x - cursor_x), - .height = ky * (CHAR_NUM_BITS_Y_ + 1), + .height = ky * (LCD_Height + 1), } ); else { @@ -881,20 +973,20 @@ void draw_text_cursor_to_buffer(Pixel_Buffer dst, i32 font, vec4_f32 color, Box color, (Box) { .x = area.x + kx * cursor_x, - .y = area.y + ky * cursor_y - ky * (CHAR_NUM_BITS_Y_ + 1), + .y = area.y + ky * cursor_y - ky * (LCD_Height + 1), .width = kx * (num_columns - cursor_x), - .height = ky * (CHAR_NUM_BITS_Y_ + 1), + .height = ky * (LCD_Height + 1), } ); - for (i64 j = cursor_y + CHAR_NUM_BITS_Y_ + 1; j < selection_y; j += CHAR_NUM_BITS_Y_ + 1) + for (i64 j = cursor_y + LCD_Height + 1; j < selection_y; j += LCD_Height + 1) fill_rectangle_to_buffer( dst, color, (Box) { .x = area.x, - .y = area.y + ky * j - ky * (CHAR_NUM_BITS_Y_ + 1), + .y = area.y + ky * j - ky * (LCD_Height + 1), .width = kx * num_columns, - .height = ky * (CHAR_NUM_BITS_Y_ + 1), + .height = ky * (LCD_Height + 1), } ); fill_rectangle_to_buffer( @@ -902,9 +994,9 @@ void draw_text_cursor_to_buffer(Pixel_Buffer dst, i32 font, vec4_f32 color, Box color, (Box) { .x = area.x, - .y = area.y + ky * selection_y - ky * (CHAR_NUM_BITS_Y_ + 1), + .y = area.y + ky * selection_y - ky * (LCD_Height + 1), .width = kx * selection_x, - .height = ky * (CHAR_NUM_BITS_Y_ + 1), + .height = ky * (LCD_Height + 1), } ); } @@ -914,9 +1006,9 @@ void draw_text_cursor_to_buffer(Pixel_Buffer dst, i32 font, vec4_f32 color, Box color, (Box) { .x = area.x + kx * cursor_x, - .y = area.y + ky * cursor_y - ky * CHAR_NUM_BITS_Y_, + .y = area.y + ky * cursor_y - ky * LCD_Height, .width = kx * .5, - .height = ky * (CHAR_NUM_BITS_Y_ - 1), + .height = ky * (LCD_Height - 1), } ); @@ -2559,6 +2651,15 @@ TEST("line") { 1.0 ); + // printf("\n"); + // for (i64 j = 0; j < h; ++j) { + // for (i64 i = 0; i < w; ++i) { + // printf(" %c", pixels[j * w + i].x > 0.5 ? 'x' : '.'); + // } + // printf("\n"); + // } + // printf("\n"); + b8 expect_bits[100] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, @@ -2608,6 +2709,15 @@ TEST("text letter A") { (c32[1]) { 'A', } ); + // printf("\n"); + // for (i64 j = 0; j < h; ++j) { + // for (i64 i = 0; i < w; ++i) { + // printf(" %c", pixels[j * w + i].x > 0.5 ? 'x' : '.'); + // } + // printf("\n"); + // } + // printf("\n"); + b8 expect_bits[100] = { 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, @@ -2657,6 +2767,15 @@ TEST("text letter z") { (c32[1]) { 'z', } ); + // printf("\n"); + // for (i64 j = 0; j < h; ++j) { + // for (i64 i = 0; i < w; ++i) { + // printf(" %c", pixels[j * w + i].x > 0.5 ? 'x' : '.'); + // } + // printf("\n"); + // } + // printf("\n"); + b8 expect_bits[100] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, @@ -2706,6 +2825,15 @@ TEST("text digits 42") { (c32[2]) { '4', '2', } ); + // printf("\n"); + // for (i64 j = 0; j < h; ++j) { + // for (i64 i = 0; i < w; ++i) { + // printf(" %c", pixels[j * w + i].x > 0.5 ? 'x' : '.'); + // } + // printf("\n"); + // } + // printf("\n"); + b8 expect_bits[100] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 1, 1, 1, 1, |