summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMitya Selivanov <automainint@guattari.tech>2025-04-26 14:34:21 +0200
committerMitya Selivanov <automainint@guattari.tech>2025-04-26 14:34:21 +0200
commitb91f3df6ad13228d993db27ff94433ba6ebce042 (patch)
tree39d0c5a8bc0f934e98b0ec81263844db7ffdecf8
parente0f99749e364de10917b2f06d41a0fd98048f74b (diff)
downloadreduced_system_layer-b91f3df6ad13228d993db27ff94433ba6ebce042.zip
Proper font dispatch procs
-rw-r--r--graphics.c300
1 files changed, 146 insertions, 154 deletions
diff --git a/graphics.c b/graphics.c
index 0ed1791..ffe620a 100644
--- a/graphics.c
+++ b/graphics.c
@@ -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,