diff options
author | Mitya Selivanov <automainint@guattari.tech> | 2025-04-26 14:34:21 +0200 |
---|---|---|
committer | Mitya Selivanov <automainint@guattari.tech> | 2025-04-26 14:34:21 +0200 |
commit | b91f3df6ad13228d993db27ff94433ba6ebce042 (patch) | |
tree | 39d0c5a8bc0f934e98b0ec81263844db7ffdecf8 | |
parent | e0f99749e364de10917b2f06d41a0fd98048f74b (diff) | |
download | reduced_system_layer-b91f3df6ad13228d993db27ff94433ba6ebce042.zip |
Proper font dispatch procs
-rw-r--r-- | graphics.c | 300 |
1 files changed, 146 insertions, 154 deletions
@@ -74,6 +74,10 @@ typedef struct { } Pixel_Buffer; enum { + FONT_LCD = 0, +}; + +enum { GRAPHICS_FILL_TRIANGLE, GRAPHICS_FILL_TRIANGLES, GRAPHICS_FILL_QUAD, @@ -137,7 +141,7 @@ typedef struct { // // ================================================================ -Box dispatch_font_text_area(i32 font, vec2 scale, i64 num_chars, c32 *text); +vec2 dispatch_font_text_area(i32 font, vec2 scale, i64 num_chars, c32 *text); void dispatch_font_render_to_stencil(Stencil_Buffer dst, i32 font, vec2 position, vec2 scale, i64 num_chars, c32 *text); @@ -186,6 +190,8 @@ void fill_triangles_to_stencil (Stencil_Buffer dst, u8 value, vec2 position, ve void fill_quad_to_stencil (Stencil_Buffer dst, u8 value, vec2 vertices[4]); void fill_ellipse_to_stencil (Stencil_Buffer dst, u8 value, Box area); void fill_line_to_stencil (Stencil_Buffer dst, u8 value, vec2 vertices[2], f64 width); +vec2 lcd_text_area (vec2 scale, i64 num_chars, c32 *text); +void lcd_text_render_to_stencil (Stencil_Buffer dst, vec2 position, vec2 scale, i64 num_chars, c32 *text); void draw_text_area_to_stencil (Stencil_Buffer dst, u8 value, i32 font, Box area, vec2 max_size, i64 num_chars, c32 *text); void draw_text_cursor_to_stencil(Stencil_Buffer dst, u8 value, i32 font, Box area, vec2 max_size, i64 num_chars, c32 *text, i64 cursor, i64 selection); @@ -622,6 +628,39 @@ Stencil_Buffer default_stencil(i64 width, i64 height) { }; } +static i64 _default_second_stencil_len = 0; +static u8 *_default_second_stencil = NULL; + +Stencil_Buffer default_second_stencil_(i64 width, i64 height) { + i64 len = width * height; + if (len < 0) + len = 1; + + if (len > _default_second_stencil_len) + resize_dynamic_array_exact(&_default_second_stencil_len, (void **) &_default_second_stencil, sizeof *_default_second_stencil, len); + + if (width <= 0 || height <= 0) + return (Stencil_Buffer) { + .width = 0, + .height = 0, + .stride = 1, + .pixels = _default_second_stencil, + }; + + i64 h = height; + if (len > _default_second_stencil_len) + h = _default_second_stencil_len / width; + + mem_set_(_default_second_stencil, 0, width * h); + + return (Stencil_Buffer) { + .width = width, + .height = h, + .stride = width, + .pixels = _default_second_stencil, + }; +} + void fill_stencil_to_stencil(Stencil_Buffer dst, u8 value, Box area, Stencil_Buffer src) { i64 x0 = (i64) floor(area.x + 0.5); i64 x1 = (i64) floor(area.x + area.width + 0.5); @@ -1293,30 +1332,6 @@ static i64 lcd_spacing_(i64 num_chars, c32 *text, i64 index) { return !!(_lcd_kerning[a * 2 + 1] & (1ull << (b - 64))); } -static i64 lcd_cursor_(i64 num_chars, c32 *text) { - if (text == NULL) - return 0; - - i64 cursor = 0; - - for (i64 i = 0; i < num_chars; ++i) { - if (text[i] <= ' ') { - if (text[i] == '\n') - cursor = 0; - else if (text[i] == '\b' && i > 0) - cursor -= lcd_char_width_(text[i - 1]) + lcd_spacing_(num_chars, text, i - 1); - else if (text[i] == '\r') - cursor = 0; - else - cursor += lcd_char_width_(' ') + lcd_spacing_(num_chars, text, i); - continue; - } - cursor += lcd_char_width_(text[i]) + lcd_spacing_(num_chars, text, i); - } - - return cursor; -} - static i64 lcd_enum_text_columns_(i64 num_chars, c32 *text) { if (text == NULL) return 0; @@ -1330,14 +1345,6 @@ static i64 lcd_enum_text_columns_(i64 num_chars, c32 *text) { if (cols < n) cols = n; n = 0; - } else if (text[i] == '\b' && i > 0) { - if (cols < n) - cols = n; - 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 += lcd_char_width_(' ') + lcd_spacing_(num_chars, text, i); continue; @@ -1352,7 +1359,7 @@ static i64 lcd_enum_text_columns_(i64 num_chars, c32 *text) { } static i64 lcd_enum_text_rows_(i64 num_chars, c32 *text) { - if (text == NULL) + if (text == NULL || num_chars == 0) return 0; i64 rows = 0; @@ -1367,27 +1374,29 @@ static i64 lcd_enum_text_rows_(i64 num_chars, c32 *text) { return rows; } -static void lcd_draw_text_(Stencil_Buffer dst, u8 value, f64 x0, f64 y0, f64 scale_x, f64 scale_y, i64 num_chars, c32 *text) { - f64 kx = scale_x; - f64 h = scale_y * LCD_Height; +vec2 lcd_text_area(vec2 scale, i64 num_chars, c32 *text) { + return (vec2) { + .x = scale.x * lcd_enum_text_columns_(num_chars, text), + .y = scale.y * lcd_enum_text_rows_ (num_chars, text), + }; +} + +void lcd_text_render_to_stencil(Stencil_Buffer dst, vec2 position, vec2 scale, i64 num_chars, c32 *text) { + f64 kx = scale.x; + f64 h = scale.y * LCD_Height; if (h < EPSILON) return; - f64 x = x0; - f64 y = y0; + f64 x = position.x; + f64 y = position.y; for (i64 n = 0; n < num_chars; ++n) { if (text[n] <= ' ') { if (text[n] == '\n') { - x = x0; - y += scale_y * (LCD_Height + 1); - } - else if (text[n] == '\b' && n > 0) - x -= kx * (lcd_char_width_(text[n - 1]) + lcd_spacing_(num_chars, text, n - 1)); - else if (text[n] == '\r') - x = x0; - else + x = position.x; + y += scale.y * (LCD_Height + 1); + } else x += kx * (lcd_char_width_(' ') + lcd_spacing_(num_chars, text, n)); continue; } @@ -1422,7 +1431,7 @@ static void lcd_draw_text_(Stencil_Buffer dst, u8 value, f64 x0, f64 y0, f64 sca u32 column = (((i - i00) * num_cols) * w_inv + w_half) >> 16; if ((_lcd_bits[index] & (1ull << (row * 8 + column))) != 0) - dst.pixels[j * dst.stride + i] = max2_u8_(dst.pixels[j * dst.stride + i], value); + dst.pixels[j * dst.stride + i] = max2_u8_(dst.pixels[j * dst.stride + i], 255); } } } @@ -1432,50 +1441,48 @@ static void lcd_draw_text_(Stencil_Buffer dst, u8 value, f64 x0, f64 y0, f64 sca } #if !defined(ENABLE_FONT_CUSTOM_DISPATCH) -Box dispatch_font_text_area(i32 font, vec2 scale, i64 num_chars, c32 *text) { - // TODO - - (void) font; - (void) scale; - (void) num_chars; - (void) text; - - return (Box) {0}; +vec2 dispatch_font_text_area(i32 font, vec2 scale, i64 num_chars, c32 *text) { + if (font == FONT_LCD) + return lcd_text_area(scale, num_chars, text); + return (vec2) {0}; } void dispatch_font_render_to_stencil(Stencil_Buffer dst, i32 font, vec2 position, vec2 scale, i64 num_chars, c32 *text) { - // TODO - - (void) dst; - (void) font; - (void) position; - (void) scale; - (void) num_chars; - (void) text; + if (font == FONT_LCD) + lcd_text_render_to_stencil(dst, position, scale, num_chars, text); } #endif // !defined(ENABLE_FONT_CUSTOM_DISPATCH) -void draw_text_area_to_stencil(Stencil_Buffer dst, u8 value, i32 font, Box area, vec2 max_size, i64 num_chars, c32 *text) { - (void) font; - - if (text == NULL || num_chars <= 0 || max_size.x < EPSILON || max_size.y < EPSILON) - return; - - i64 num_columns = lcd_enum_text_columns_(num_chars, text); - i64 num_rows = lcd_enum_text_rows_(num_chars, text); +static vec2 adjust_text_scale_(i32 font, vec2 area, vec2 max_size, i64 num_chars, c32 *text) { + vec2 orig_area = dispatch_font_text_area(font, (vec2) { 1.0, 1.0 }, num_chars, text); + if (orig_area.x < EPSILON || orig_area.y < EPSILON) + return (vec2) {0}; - f64 scale_x = area.width / num_columns; - f64 scale_y = area.height / num_rows; + f64 scale_x = area.x / orig_area.x; + f64 scale_y = area.y / orig_area.y; f64 kx = scale_x / max_size.x; f64 ky = scale_y / max_size.y; f64 k = kx < ky ? kx : ky; - kx = k * max_size.x; - ky = k * max_size.y; + return (vec2) { k * max_size.x, k * max_size.y }; +} - lcd_draw_text_(dst, value, area.x, area.y, kx, ky, num_chars, text); +void draw_text_area_to_stencil(Stencil_Buffer dst, u8 value, i32 font, Box area, vec2 max_size, i64 num_chars, c32 *text) { + (void) font; + + if (text == NULL || num_chars <= 0 || max_size.x < EPSILON || max_size.y < EPSILON) + return; + + i64 w = (i64) floor(area.width + 0.5); + i64 h = (i64) floor(area.height + 0.5); + + vec2 scale = adjust_text_scale_(font, (vec2) { area.width, area.height }, max_size, num_chars, text); + + Stencil_Buffer buf = default_second_stencil_(w, h); + dispatch_font_render_to_stencil(buf, font, (vec2) {0}, scale, num_chars, text); + fill_stencil_to_stencil(dst, value, area, buf); } void draw_text_cursor_to_stencil(Stencil_Buffer dst, u8 value, i32 font, Box area, vec2 max_size, i64 num_chars, c32 *text, i64 cursor, i64 selection) { @@ -1484,90 +1491,75 @@ void draw_text_cursor_to_stencil(Stencil_Buffer dst, u8 value, i32 font, Box are if (max_size.x < EPSILON || max_size.y < EPSILON) return; - 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); + if (cursor < 0 || cursor + selection < 0 || cursor > num_chars || cursor + selection > num_chars) { + LOG_error("Sanity"); + return; + } - f64 scale_x = area.width / num_columns; - f64 scale_y = area.height / num_rows; + vec2 scale = adjust_text_scale_(font, (vec2) { area.width, area.height }, max_size, num_chars, text); - f64 kx = scale_x / max_size.x; - f64 ky = scale_y / max_size.y; + if (selection != 0) { + i64 s0 = selection < 0 ? cursor + selection : cursor; + i64 s1 = selection > 0 ? cursor + selection : cursor; - f64 k = kx < ky ? kx : ky; + i64 prev_line = 0; - kx = k * max_size.x; - ky = k * max_size.y; + for (i64 i0 = 0; i0 < num_chars; ++i0) { + i64 i1 = i0; + for (; i1 < num_chars && text[i1] != '\n'; ++i1); - if (selection != 0) { - i64 selection_x, selection_y; + f64 x0 = 0.0, x1 = -1.0, y0 = 0.0, y1 = -1.0; - if (selection > 0) { - 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 = lcd_cursor_(cursor + selection, text); - cursor_y = lcd_enum_text_rows_(cursor + selection, text); + if (s0 >= i0 && s0 < i1) { + // Beginning of the selected fragment. + + x0 = dispatch_font_text_area(font, scale, s0 - i0, text + i0).x; + x1 = dispatch_font_text_area(font, scale, i1 - i0, text + i0).x; + y0 = dispatch_font_text_area(font, scale, prev_line, text).y + 0.5; + y1 = dispatch_font_text_area(font, scale, i1, text).y; + } + + if (s1 >= i0 && s1 < i1) { + // Tail of the selected fragment. + + x1 = dispatch_font_text_area(font, scale, s1 - i0, text + i0).x; + y0 = dispatch_font_text_area(font, scale, prev_line, text).y + 0.5; + y1 = dispatch_font_text_area(font, scale, i1, text).y; + } + + if (i0 >= s0 && i1 <= s1) { + // Middle of the selected fragment. + + x1 = dispatch_font_text_area(font, scale, i1 - i0, text + i0).x; + y0 = dispatch_font_text_area(font, scale, prev_line, text).y + 0.5; + y1 = dispatch_font_text_area(font, scale, i1, text).y; + } + + if (x1 - x0 > EPSILON && y1 - y0 > EPSILON) + fill_rectangle_to_stencil(dst, value, (Box) { .x = x0, .y = y0, .width = x1 - x0, .height = y1 - y0, }); + + prev_line = i1; + i0 = i1; } + } else { + f64 x0 = 0.0, x1 = -1.0, y0 = 0.0, y1 = -1.0; - if (cursor_y == selection_y) - fill_rectangle_to_stencil( - dst, - value, - (Box) { - .x = area.x + kx * cursor_x, - .y = area.y + ky * cursor_y - ky * LCD_Height, - .width = kx * (selection_x - cursor_x), - .height = ky * (LCD_Height + 1), - } - ); - else { - fill_rectangle_to_stencil( - dst, - value, - (Box) { - .x = area.x + kx * cursor_x, - .y = area.y + ky * cursor_y - ky * LCD_Height, - .width = kx * (num_columns - cursor_x), - .height = ky * (LCD_Height + 1), - } - ); - for (i64 j = cursor_y + LCD_Height + 1; j < selection_y; j += LCD_Height + 1) - fill_rectangle_to_stencil( - dst, - value, - (Box) { - .x = area.x, - .y = area.y + ky * j - ky * LCD_Height, - .width = kx * num_columns, - .height = ky * (LCD_Height + 1), - } - ); - fill_rectangle_to_stencil( - dst, - value, - (Box) { - .x = area.x, - .y = area.y + ky * selection_y - ky * LCD_Height, - .width = kx * selection_x, - .height = ky * (LCD_Height + 1), - } - ); + if (num_chars == 0) { + x1 = scale.x; + y1 = dispatch_font_text_area(font, scale, 1, (c32[1]) { '|' }).y; + } else { + i64 i0 = 0; + for (i64 i = 0; i < cursor; ++i) + if (text[i] == '\n') + i0 = i + 1; + x0 = dispatch_font_text_area(font, scale, cursor - i0, text + i0).x; + x1 = x0 + scale.x; + y0 = dispatch_font_text_area(font, scale, i0 > 0 ? i0 - 1 : 0, text).y; + y1 = dispatch_font_text_area(font, scale, cursor, text).y; } - } else - fill_rectangle_to_stencil( - dst, - value, - (Box) { - .x = area.x + kx * cursor_x, - .y = area.y + ky * cursor_y - ky * LCD_Height, - .width = kx * 0.5, - .height = ky * (LCD_Height - 1), - } - ); + + fill_rectangle_to_stencil(dst, value, (Box) { .x = x0, .y = y0, .width = x1 - x0, .height = y1 - y0, }); + } } void draw_text_area_to_buffer(Pixel_Buffer dst, vec4_f32 color, i32 font, Box area, vec2 max_size, i64 num_chars, c32 *text, Stencil_Buffer stencil) { @@ -3151,7 +3143,7 @@ TEST("text cursor") { 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, |