From 329915ac1bf45f21e5f2ea27b05ba30ccd78915e Mon Sep 17 00:00:00 2001 From: lisyarus Date: Wed, 18 May 2022 22:45:35 +0300 Subject: [PATCH] Rich text label wip: store cached label state as a series of batches of texture views (NB: transparent labels are broken) --- libs/ui/include/psemek/ui/label.hpp | 15 +++++++-- libs/ui/source/label.cpp | 51 ++++++++++++++++++++--------- 2 files changed, 48 insertions(+), 18 deletions(-) diff --git a/libs/ui/include/psemek/ui/label.hpp b/libs/ui/include/psemek/ui/label.hpp index 1c866f06..1ea6c44c 100644 --- a/libs/ui/include/psemek/ui/label.hpp +++ b/libs/ui/include/psemek/ui/label.hpp @@ -95,8 +95,19 @@ namespace psemek::ui struct cached_state { - struct font const * font = nullptr; - std::vector glyphs; + struct image + { + geom::box texcoords; + geom::box position; + }; + + struct batch + { + gfx::texture_2d const * texture; + std::vector images; + }; + + std::vector batches; geom::vector size{0.f, 0.f}; }; diff --git a/libs/ui/source/label.cpp b/libs/ui/source/label.cpp index e3c85cd9..67400fc4 100644 --- a/libs/ui/source/label.cpp +++ b/libs/ui/source/label.cpp @@ -103,17 +103,20 @@ namespace psemek::ui if (!cached_state_) update_cached_state(); - if (!cached_state_->font) return; - auto st = merged_own_style(); if (!st) return; if (st->text_shadow_offset != geom::vector{0, 0}) - for (auto & g : cached_state_->glyphs) - p.draw_glyph(*(cached_state_->font), g.character, g.position + geom::cast(*st->text_shadow_offset), *st->shadow_color); + { + auto const offset = geom::cast(*st->text_shadow_offset); + for (auto const & batch : cached_state_->batches) + for (auto const & image : batch.images) + p.draw_image(image.position + offset, gfx::texture_view_2d{batch.texture, image.texcoords}, *st->shadow_color); + } - for (auto & g : cached_state_->glyphs) - p.draw_glyph(*(cached_state_->font), g.character, g.position, *st->text_color); + for (auto const & batch : cached_state_->batches) + for (auto const & image : batch.images) + p.draw_image(image.position, gfx::texture_view_2d{batch.texture, image.texcoords}, *st->text_color); } void label::on_state_changed() @@ -142,13 +145,13 @@ namespace psemek::ui auto st = merged_own_style(); if (!st) return state; - state.font = (font_ == font_type::normal) ? st->font.get() : st->bold_font.get(); + auto font = (font_ == font_type::normal) ? st->font.get() : st->bold_font.get(); - if (!state.font) return state; + if (!font) return state; shape_options opts; opts.scale = *st->text_scale; - auto glyphs = state.font->shape(text_, opts); + auto glyphs = font->shape(text_, opts); geom::box raw_bbox; for (auto const & g : glyphs) @@ -159,7 +162,7 @@ namespace psemek::ui if (wrap_) { max_lines = std::isfinite(bbox[1].length()) - ? std::max(1, std::floor(bbox[1].length() / state.font->size()[1] / (*st->text_scale))) + ? std::max(1, std::floor(bbox[1].length() / font->size()[1] / (*st->text_scale))) : std::numeric_limits::max(); } @@ -224,7 +227,7 @@ namespace psemek::ui x_offset = glyphs[el_start - 1].position[0].max; char const el_str[] = "..."; - auto els = state.font->shape(el_str, opts); + auto els = font->shape(el_str, opts); for (std::size_t i = el_start; i < line_end; ++i) { glyphs[i] = els[i - el_start]; @@ -283,13 +286,13 @@ namespace psemek::ui switch (valign_) { case valignment::top: - offset[1] = bbox[1].min + l * (*st->text_scale) * state.font->size()[1]; + offset[1] = bbox[1].min + l * (*st->text_scale) * font->size()[1]; break; case valignment::center: - offset[1] = bbox[1].center() + (l - lines.size() / 2.f) * (*st->text_scale) * state.font->size()[1]; + offset[1] = bbox[1].center() + (l - lines.size() / 2.f) * (*st->text_scale) * font->size()[1]; break; case valignment::bottom: - offset[1] = bbox[1].max + (l - lines.size() * 1.f) * (*st->text_scale) * state.font->size()[1]; + offset[1] = bbox[1].max + (l - lines.size() * 1.f) * (*st->text_scale) * font->size()[1]; break; } @@ -301,9 +304,25 @@ namespace psemek::ui } } - state.glyphs = std::move(glyphs); + { + auto & batch = state.batches.emplace_back(); + batch.texture = &font->atlas(); + + for (auto const & g : glyphs) + { + auto tc = font->texcoords(g.character); + if (!tc) continue; + + auto & image = batch.images.emplace_back(); + image.position = g.position; + image.position[0] += (std::round(image.position[0].min) - image.position[0].min); + image.position[1] += (std::round(image.position[1].min) - image.position[1].min); + image.texcoords = geom::cast(*tc); + } + } + state.size[0] = max_line_size; - state.size[1] = lines.size() * (*st->text_scale) * state.font->size()[1]; + state.size[1] = lines.size() * (*st->text_scale) * font->size()[1]; return state; }