Rich text label wip: store cached label state as a series of batches of texture views (NB: transparent labels are broken)

This commit is contained in:
Nikita Lisitsa 2022-05-18 22:45:35 +03:00
parent 59de6682ba
commit 329915ac1b
2 changed files with 48 additions and 18 deletions

View file

@ -95,8 +95,19 @@ namespace psemek::ui
struct cached_state struct cached_state
{ {
struct font const * font = nullptr; struct image
std::vector<glyph> glyphs; {
geom::box<std::size_t, 2> texcoords;
geom::box<float, 2> position;
};
struct batch
{
gfx::texture_2d const * texture;
std::vector<image> images;
};
std::vector<batch> batches;
geom::vector<float, 2> size{0.f, 0.f}; geom::vector<float, 2> size{0.f, 0.f};
}; };

View file

@ -103,17 +103,20 @@ namespace psemek::ui
if (!cached_state_) if (!cached_state_)
update_cached_state(); update_cached_state();
if (!cached_state_->font) return;
auto st = merged_own_style(); auto st = merged_own_style();
if (!st) return; if (!st) return;
if (st->text_shadow_offset != geom::vector{0, 0}) 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<float>(*st->text_shadow_offset), *st->shadow_color); auto const offset = geom::cast<float>(*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) for (auto const & batch : cached_state_->batches)
p.draw_glyph(*(cached_state_->font), g.character, g.position, *st->text_color); 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() void label::on_state_changed()
@ -142,13 +145,13 @@ namespace psemek::ui
auto st = merged_own_style(); auto st = merged_own_style();
if (!st) return state; 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; shape_options opts;
opts.scale = *st->text_scale; opts.scale = *st->text_scale;
auto glyphs = state.font->shape(text_, opts); auto glyphs = font->shape(text_, opts);
geom::box<float, 2> raw_bbox; geom::box<float, 2> raw_bbox;
for (auto const & g : glyphs) for (auto const & g : glyphs)
@ -159,7 +162,7 @@ namespace psemek::ui
if (wrap_) if (wrap_)
{ {
max_lines = std::isfinite(bbox[1].length()) max_lines = std::isfinite(bbox[1].length())
? std::max<std::size_t>(1, std::floor(bbox[1].length() / state.font->size()[1] / (*st->text_scale))) ? std::max<std::size_t>(1, std::floor(bbox[1].length() / font->size()[1] / (*st->text_scale)))
: std::numeric_limits<std::size_t>::max(); : std::numeric_limits<std::size_t>::max();
} }
@ -224,7 +227,7 @@ namespace psemek::ui
x_offset = glyphs[el_start - 1].position[0].max; x_offset = glyphs[el_start - 1].position[0].max;
char const el_str[] = "..."; 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) for (std::size_t i = el_start; i < line_end; ++i)
{ {
glyphs[i] = els[i - el_start]; glyphs[i] = els[i - el_start];
@ -283,13 +286,13 @@ namespace psemek::ui
switch (valign_) switch (valign_)
{ {
case valignment::top: 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; break;
case valignment::center: 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; break;
case valignment::bottom: 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; 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<std::size_t>(*tc);
}
}
state.size[0] = max_line_size; 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; return state;
} }