summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJonathan Bradley <jcb@pikum.xyz>2025-09-23 09:44:19 -0400
committerJonathan Bradley <jcb@pikum.xyz>2025-09-23 09:44:31 -0400
commitb1409537eb5a3bbdbae892cf3f6a451dee5472a9 (patch)
treea8b6f04eda5f803a5993bcc66fbe25eaea560790
parent2e72d3b1b2688f2f20a313885e1bf90eedc41093 (diff)
pke: ui box text center first-pass
-rw-r--r--src/font.cpp78
-rw-r--r--src/font.hpp41
-rw-r--r--tests/pke-test-serialization.cpp6
3 files changed, 97 insertions, 28 deletions
diff --git a/src/font.cpp b/src/font.cpp
index 3dba635..3204f58 100644
--- a/src/font.cpp
+++ b/src/font.cpp
@@ -108,6 +108,47 @@ float FontType_Inner_LookAheadWordLength(const FontType *const ft, const FontRen
return ret;
}
+float FontType_Inner_LookAheadLineLength(const FontType *const ft, const FontRender *const fr, uint32_t index, float font_glyph_spacing) {
+ uint32_t i;
+ float ret = 0;
+ FontGlyphChar *fgc;
+ for (i = index; i < fr->n_glyphs; ++i) {
+ fgc = &ft->glyphs[fr->glyph_indices[i]];
+ if (PK_HAS_FLAG(fgc->flags, FONT_GLYPH_CHAR_FLAGS_NEW_LINE) == true) {
+ break;
+ }
+ if (ret + (fgc->advance * font_glyph_spacing) > fr->settings.surface_area_size.x) {
+ break;
+ }
+ ret += fgc->advance * font_glyph_spacing;
+ }
+ return ret;
+}
+
+float FontType_Inner_LookAheadLineCount(const FontType *const ft, const FontRender *const fr, const uint32_t index) {
+ uint32_t i, u;
+ float ret = 1;
+ FontGlyphChar *fgc;
+ for (i = index; i < fr->n_glyphs; ++i) {
+ fgc = &ft->glyphs[fr->glyph_indices[i]];
+ if (PK_HAS_FLAG(fgc->flags, FONT_GLYPH_CHAR_FLAGS_NEW_LINE) == true) {
+ if (i+1 < fr->n_glyphs) {
+ if (fgc->unicode == 10) {
+ u = 11;
+ } else {
+ u = 10;
+ }
+ // handle \r\n
+ if (ft->glyphs[fr->glyph_indices[i+1]].unicode == u) {
+ i += 1; // burn
+ }
+ }
+ ret += 1;
+ }
+ }
+ return ret;
+}
+
void FontType_Inner_CalcTransforms(const FontType *ft, FontRender *fr, FontInstanceBufferItem *ptr_dst) {
assert(ft != nullptr);
assert(fr != nullptr);
@@ -117,7 +158,7 @@ void FontType_Inner_CalcTransforms(const FontType *ft, FontRender *fr, FontInsta
bool new_word = true;
uint32_t i;
float font_glyph_spacing;
- float cursor_head, line_index, line_height;
+ float cursor_head, line_index, line_height, line_length, line_offset;
glm::vec2 glyph_size;
glm::vec3 translate;
glm::vec3 scale;
@@ -129,12 +170,31 @@ void FontType_Inner_CalcTransforms(const FontType *ft, FontRender *fr, FontInsta
// Trying to get pixel-perfect rendering with mannequin-7.
// Everything is now sized and positioned correctly.
// However, there is a ghost layer around the letter.
+ // 2025-09-22 - JCB
+ // I believe this is because of how the anti-aliasing works.
+ // When this gets re-visited, attempt to disable anti-aliasing entirely.
+ // Suggestion: require min distance to be 0?
+ // Also note that I don't think we're doing any calculations to make sure the
+ // left-most pixel is exact to the Extent (screen size).
font_glyph_spacing = ft->spacing.em_size * fr->settings.char_scale;
cursor_head = 0;
line_index = 0;
+ line_offset = 0;
line_height = font_glyph_spacing * ft->spacing.line_height * fr->settings.line_height_scale;
+ line_length = FontType_Inner_LookAheadLineLength(ft, fr, 0, font_glyph_spacing);
+ if (PK_HAS_FLAG(fr->settings.surface_area_type_flags, FONT_RENDER_SURFACE_AREA_TYPE_FLAGS_CENTER_HORIZONTAL)) {
+ cursor_head = (fr->settings.surface_area_size.x - line_length) / 2.0;
+ }
+ if (PK_HAS_FLAG(fr->settings.surface_area_type_flags, FONT_RENDER_SURFACE_AREA_TYPE_FLAGS_CENTER_VERTICAL)) {
+ float text_height = FontType_Inner_LookAheadLineCount(ft, fr, 0) * line_height;
+ // TODO
+ // This is wrong but I'm not sure how/why.
+ // Mathematically it should be ` / 2.0`.
+ line_offset += (fr->settings.surface_area_size.y - text_height) / 4.0;
+ }
+
for (i = 0; i < fr->n_glyphs; ++i) {
translate = glm::vec3(0);
scale = glm::vec3(1);
@@ -149,7 +209,12 @@ void FontType_Inner_CalcTransforms(const FontType *ft, FontRender *fr, FontInsta
}
}
line_index += 1;
- cursor_head = 0;
+ line_length = FontType_Inner_LookAheadLineLength(ft, fr, i+1, font_glyph_spacing);
+ if (PK_HAS_FLAG(fr->settings.surface_area_type_flags, FONT_RENDER_SURFACE_AREA_TYPE_FLAGS_CENTER_HORIZONTAL)) {
+ cursor_head = (fr->settings.surface_area_size.x - line_length) / 2.0;
+ } else {
+ cursor_head = 0;
+ }
continue;
}
if (PK_HAS_FLAG(fgc->flags, FONT_GLYPH_CHAR_FLAGS_WHITESPACE) == true) {
@@ -172,7 +237,13 @@ void FontType_Inner_CalcTransforms(const FontType *ft, FontRender *fr, FontInsta
float word_width = FontType_Inner_LookAheadWordLength(ft, fr, i, font_glyph_spacing);
if (cursor_head + word_width > fr->settings.surface_area_size.x) {
line_index += 1;
- cursor_head = 0;
+ line_length = FontType_Inner_LookAheadLineLength(ft, fr, i+1, font_glyph_spacing);
+
+ if (PK_HAS_FLAG(fr->settings.surface_area_type_flags, FONT_RENDER_SURFACE_AREA_TYPE_FLAGS_CENTER_HORIZONTAL)) {
+ cursor_head = (fr->settings.surface_area_size.x - line_length) / 2.0;
+ } else {
+ cursor_head = 0;
+ }
}
new_word = false;
}
@@ -207,6 +278,7 @@ void FontType_Inner_CalcTransforms(const FontType *ft, FontRender *fr, FontInsta
translate.y = fr->settings.surface_area_pos.y;
// baseline - current line (+1 to not draw above the box)
translate.y += ((line_index + 1) * line_height);
+ translate.y += line_offset;
// places the top line of the glyph on the baseline
translate.y += (font_glyph_spacing / 2.0) + (glyph_size.y / 2.0);
// move glyph to the height relative to the baseline (cursor)
diff --git a/src/font.hpp b/src/font.hpp
index 75035b8..fce99a0 100644
--- a/src/font.hpp
+++ b/src/font.hpp
@@ -16,28 +16,25 @@ TypeSafeInt_H(FontRenderIndex, uint16_t, 0xFFFF);
TypeSafeInt_H(FONT_GLYPH_CHAR_FLAG, uint8_t, 0xFF);
TypeSafeInt_H(FONT_RENDER_SURFACE_AREA_TYPE_FLAG, uint8_t, 0xFF);
-constexpr FONT_GLYPH_CHAR_FLAG FONT_GLYPH_CHAR_FLAGS_NONE
- = FONT_GLYPH_CHAR_FLAG((0 << 0));
-constexpr FONT_GLYPH_CHAR_FLAG FONT_GLYPH_CHAR_FLAGS_CONTROL
- = FONT_GLYPH_CHAR_FLAG((1 << 0));
-constexpr FONT_GLYPH_CHAR_FLAG FONT_GLYPH_CHAR_FLAGS_WHITESPACE
- = FONT_GLYPH_CHAR_FLAG((1 << 1));
-constexpr FONT_GLYPH_CHAR_FLAG FONT_GLYPH_CHAR_FLAGS_ALIGN_ADVANCE
- = FONT_GLYPH_CHAR_FLAG((1 << 2));
-constexpr FONT_GLYPH_CHAR_FLAG FONT_GLYPH_CHAR_FLAGS_NEW_LINE
- = FONT_GLYPH_CHAR_FLAG((1 << 3));
+const FONT_GLYPH_CHAR_FLAG FONT_GLYPH_CHAR_FLAGS_NONE
+ = FONT_GLYPH_CHAR_FLAG(0u);
+const FONT_GLYPH_CHAR_FLAG FONT_GLYPH_CHAR_FLAGS_CONTROL
+ = FONT_GLYPH_CHAR_FLAG((1u << 0));
+const FONT_GLYPH_CHAR_FLAG FONT_GLYPH_CHAR_FLAGS_WHITESPACE
+ = FONT_GLYPH_CHAR_FLAG((1u << 1));
+const FONT_GLYPH_CHAR_FLAG FONT_GLYPH_CHAR_FLAGS_ALIGN_ADVANCE
+ = FONT_GLYPH_CHAR_FLAG((1u << 2));
+const FONT_GLYPH_CHAR_FLAG FONT_GLYPH_CHAR_FLAGS_NEW_LINE
+ = FONT_GLYPH_CHAR_FLAG((1u << 3));
-constexpr FONT_RENDER_SURFACE_AREA_TYPE_FLAG FONT_RENDER_SURFACE_AREA_TYPE_FLAGS_NONE
- = FONT_RENDER_SURFACE_AREA_TYPE_FLAG(0);
-constexpr FONT_RENDER_SURFACE_AREA_TYPE_FLAG FONT_RENDER_SURFACE_AREA_TYPE_FLAGS_GROW_VERTICAL
- = FONT_RENDER_SURFACE_AREA_TYPE_FLAG((1 << 0));
-constexpr FONT_RENDER_SURFACE_AREA_TYPE_FLAG FONT_RENDER_SURFACE_AREA_TYPE_FLAGS_GROW_HORIZONTAL
- = FONT_RENDER_SURFACE_AREA_TYPE_FLAG((1 << 1));
-
-constexpr FONT_RENDER_SURFACE_AREA_TYPE_FLAG FONT_RENDER_SURFACE_AREA_TYPE_FLAGS_STATIC
- = FONT_RENDER_SURFACE_AREA_TYPE_FLAG(0);
-constexpr FONT_RENDER_SURFACE_AREA_TYPE_FLAG FONT_RENDER_SURFACE_AREA_TYPE_FLAGS_FLUID
- = FONT_RENDER_SURFACE_AREA_TYPE_FLAG((1 << 0) | (1 << 1));
+const FONT_RENDER_SURFACE_AREA_TYPE_FLAG FONT_RENDER_SURFACE_AREA_TYPE_FLAGS_NONE
+ = FONT_RENDER_SURFACE_AREA_TYPE_FLAG(0u);
+const FONT_RENDER_SURFACE_AREA_TYPE_FLAG FONT_RENDER_SURFACE_AREA_TYPE_FLAGS_CENTER_VERTICAL
+ = FONT_RENDER_SURFACE_AREA_TYPE_FLAG((1u << 0));
+const FONT_RENDER_SURFACE_AREA_TYPE_FLAG FONT_RENDER_SURFACE_AREA_TYPE_FLAGS_CENTER_HORIZONTAL
+ = FONT_RENDER_SURFACE_AREA_TYPE_FLAG((1u << 1));
+const FONT_RENDER_SURFACE_AREA_TYPE_FLAG FONT_RENDER_SURFACE_AREA_TYPE_FLAGS_CENTER_BOTH
+ = FONT_RENDER_SURFACE_AREA_TYPE_FLAG((1u << 0) | (1u << 1));
struct FontRenderHandle {
FontTypeIndex index_ft;
@@ -65,7 +62,7 @@ struct FontRenderSettings {
float char_spacing_scale = 1.f;
glm::ivec2 surface_area_size;
glm::ivec2 surface_area_pos;
- FONT_RENDER_SURFACE_AREA_TYPE_FLAG surface_area_type_flags; // TODO
+ FONT_RENDER_SURFACE_AREA_TYPE_FLAG surface_area_type_flags;
};
struct FontRender : public Entity_Base {
uint32_t *glyph_indices = nullptr;
diff --git a/tests/pke-test-serialization.cpp b/tests/pke-test-serialization.cpp
index de483f7..0d97174 100644
--- a/tests/pke-test-serialization.cpp
+++ b/tests/pke-test-serialization.cpp
@@ -411,7 +411,7 @@ FontRenderUUID:01010101-0101-0101-0101-010101010101
UIBox:00000000!00000003
ChildId:00000000!00000002
UUID:02020202-0202-0202-0202-020202020202
-Flags:0x84
+Flags:0x3
PosTopLeft:0.100000;0.100000
MaxSize:0.800000;0.800000
Type:0x32
@@ -434,7 +434,7 @@ int pke_test_serialization_003() {
frs.line_height_scale = 1.0;
frs.surface_area_size = glm::ivec2(250, 250);
frs.surface_area_pos = glm::ivec2(0, 0);
- frs.surface_area_type_flags = FONT_RENDER_SURFACE_AREA_TYPE_FLAGS_FLUID;
+ frs.surface_area_type_flags = FONT_RENDER_SURFACE_AREA_TYPE_FLAGS_CENTER_BOTH;
FontRenderHandle fr_handle = FontType_AddStringRender(FontType_GetFonts(idx_unused)[0].index_ft, std::move(cstring_to_pk_cstr("asdf")), &frs, nullptr, uuid_n[1]);
pke_ui_box *ui_box = pke_ui_box_new_root(PKE_UI_BOX_TYPE_TEXT, uuid_n[2]);
@@ -541,7 +541,7 @@ int pke_test_deserialization_103() {
PKE_TEST_ASSERT(fr->settings.char_spacing_scale == 1.f, err_index);
PKE_TEST_ASSERT(fr->settings.surface_area_size == glm::ivec2(250,250), err_index);
PKE_TEST_ASSERT(fr->settings.surface_area_pos == glm::ivec2(0,0), err_index);
- PKE_TEST_ASSERT(fr->settings.surface_area_type_flags == FONT_RENDER_SURFACE_AREA_TYPE_FLAGS_FLUID, err_index);
+ PKE_TEST_ASSERT(fr->settings.surface_area_type_flags == FONT_RENDER_SURFACE_AREA_TYPE_FLAGS_CENTER_BOTH, err_index);
// auto equal_4 = glm::epsilonEqual(fr->settings.color_foreground, glm::vec4(0.4, 0.9, 0.5, 0.8), epsilon_4);
// PKE_TEST_ASSERT(equal_4 == bool_4, err_index);
PKE_TEST_ASSERT(fr->settings.color_foreground == glm::vec4(0.4, 0.9, 0.5, 0.8), err_index);