Text rendering with line wrapping inside of area

This commit is contained in:
mrkubax10 2023-07-01 13:49:50 +02:00
parent e9e05d5ed1
commit 29d8bf474a
2 changed files with 122 additions and 36 deletions

View File

@ -68,36 +68,105 @@ void Font::render_glyphs(const std::string& text, unsigned size, const glm::vec4
std::wstring_convert<std::codecvt_utf8<char32_t>, char32_t> converter;
std::u32string converted = converter.from_bytes(text);
unsigned x_pos = 0;
unsigned text_line_y = 0;
for(char32_t ch : converted) {
output.push_back(std::move(render_glyph(ch, size, color)));
RasterizedGlyph* glyph = output.back().get();
if(glyph->m_height>text_line_y)
text_line_y = glyph->m_height;
}
unsigned x_pos = 0;
for(std::unique_ptr<RasterizedGlyph>& glyph : output) {
glyph->m_x = x_pos;
glyph->m_y = 0; // TODO: Update this when multiline text rendering will be supported
x_pos+=glyph->m_advance_x;
glyph->m_y = text_line_y - glyph->m_height;
x_pos+=glyph->m_width;
}
}
std::unique_ptr<Surface> Font::render_text(const std::vector<std::unique_ptr<RasterizedGlyph>>& glyphs) {
void Font::render_glyphs_in_area(const glm::vec2& area_size, const std::string& text, unsigned size, const glm::vec4ub& color, std::vector<std::unique_ptr<RasterizedGlyph>>& output) {
if(text.empty())
return;
if(size!=m_prev_size) {
if(FT_Set_Char_Size(m_face, 0, size*64, 0, 0)) {
LOG_ERROR("Failed to set size %d for Font %p", size, this);
return;
}
m_prev_size = size;
}
std::wstring_convert<std::codecvt_utf8<char32_t>, char32_t> converter;
std::u32string converted = converter.from_bytes(text);
unsigned x_pos = 0;
unsigned y_pos = 0;
unsigned text_line_y = 0;
unsigned glyph_in_line_count = 0;
std::vector<GlyphPositionInfo> glyph_positions;
for(char32_t ch : converted) {
output.push_back(std::move(render_glyph(ch, size, color)));
RasterizedGlyph* glyph = output.back().get();
x_pos+=glyph->m_width;
if(y_pos>area_size.y)
break;
if(x_pos+glyph->m_width<area_size.x) {
if(glyph->m_height>text_line_y)
text_line_y = glyph->m_height;
}
else {
x_pos = 0;
glyph_positions.push_back(GlyphPositionInfo{text_line_y, glyph_in_line_count});
glyph_in_line_count = 0;
y_pos+=text_line_y;
text_line_y = 0;
}
glyph_in_line_count++;
}
if(glyph_in_line_count>0)
glyph_positions.push_back(GlyphPositionInfo{text_line_y, glyph_in_line_count});
size_t glyph_counter = 0;
unsigned glyph_line = 0;
x_pos = 0;
y_pos = 0;
for(std::unique_ptr<RasterizedGlyph>& glyph : output) {
const GlyphPositionInfo& position_info = glyph_positions[glyph_line];
glyph->m_x = x_pos;
glyph->m_y = y_pos+position_info.m_text_line_y-glyph->m_height;
x_pos+=glyph->m_width;
glyph_counter++;
if(glyph_counter==position_info.m_glyph_count) {
glyph_counter = 0;
x_pos = 0;
y_pos+=position_info.m_text_line_y+12;
glyph_line++;
}
}
}
std::unique_ptr<Surface> Font::render_text(const std::vector<std::unique_ptr<RasterizedGlyph>>& glyphs, const glm::vec2& final_size) {
std::unique_ptr<Surface> result;
unsigned final_surface_width = 0;
int final_surface_height = 0;
int surface_text_line = 0;
for(const std::unique_ptr<RasterizedGlyph>& glyph : glyphs) {
final_surface_width+=glyph->m_advance_x;
if(static_cast<unsigned>(surface_text_line)<glyph->m_surface->get_height())
surface_text_line = glyph->m_surface->get_height();
const int current_surface_height = surface_text_line-glyph->m_top+glyph->m_surface->get_height();
if(current_surface_height>final_surface_height)
final_surface_height = current_surface_height;
unsigned final_surface_height = 0;
if(final_size.x!=0 && final_size.y!=0) {
final_surface_width = final_size.x;
final_surface_height = final_size.y;
}
else {
for(const std::unique_ptr<RasterizedGlyph>& glyph : glyphs) {
const unsigned right_point = glyph->m_x+glyph->m_width;
const unsigned bottom_point = glyph->m_y+glyph->m_height;
if(right_point>final_surface_width)
final_surface_width = right_point;
if(bottom_point>final_surface_height)
final_surface_height = bottom_point;
}
}
result = std::make_unique<Surface>(final_surface_width, final_surface_height, glm::vec4ub(0, 0, 0, 0));
unsigned x_pos = 0;
for(const std::unique_ptr<RasterizedGlyph>& glyph : glyphs) {
result->blit_surface(glyph->m_x, surface_text_line-glyph->m_top, *glyph->m_surface);
x_pos+=glyph->m_advance_x;
}
for(const std::unique_ptr<RasterizedGlyph>& glyph : glyphs)
result->blit_surface(glyph->m_x, glyph->m_y, *glyph->m_surface);
return result;
}
@ -109,6 +178,13 @@ std::unique_ptr<Surface> Font::render_text(const std::string& text, unsigned siz
return render_text(character_surfaces);
}
std::unique_ptr<Surface> Font::render_text_in_area(const glm::vec2& area_size, const std::string& text, unsigned size, const glm::vec4ub& color) {
std::vector<std::unique_ptr<RasterizedGlyph>> character_surfaces;
render_glyphs_in_area(area_size, text, size, color, character_surfaces);
return render_text(character_surfaces, area_size);
}
std::unique_ptr<Font::RasterizedGlyph> Font::render_glyph(unsigned charcode, unsigned size, const glm::vec4ub& color) {
std::unique_ptr<RasterizedGlyph> result = std::make_unique<Font::RasterizedGlyph>();
@ -127,25 +203,27 @@ std::unique_ptr<Font::RasterizedGlyph> Font::render_glyph(unsigned charcode, uns
uint8_t* pixels = new uint8_t[pixels_size];
memset(pixels, 0, pixels_size);
switch(bitmap.pixel_mode) {
case FT_PIXEL_MODE_NONE:
LOG_ERROR("Invalid glyph pixel mode");
return result;
case FT_PIXEL_MODE_MONO:
convert_ft_mono_pixels(bitmap, color, pixels);
break;
case FT_PIXEL_MODE_GRAY:
convert_ft_gray_pixels(bitmap, color, pixels);
break;
default:
LOG_ERROR("(FIXME) Unsupported glyph pixel mode: %d", bitmap.pixel_mode);
return result;
if(!isspace(charcode)) {
switch(bitmap.pixel_mode) {
case FT_PIXEL_MODE_NONE:
LOG_ERROR("Invalid glyph pixel mode");
return result;
case FT_PIXEL_MODE_MONO:
convert_ft_mono_pixels(bitmap, color, pixels);
break;
case FT_PIXEL_MODE_GRAY:
convert_ft_gray_pixels(bitmap, color, pixels);
break;
default:
LOG_ERROR("(FIXME) Unsupported glyph pixel mode: %d", bitmap.pixel_mode);
return result;
}
}
result->m_surface = std::make_unique<Surface>(bitmap.width, bitmap.rows, pixels);
delete[] pixels;
result->m_advance_x = m_face->glyph->advance.x>>6;
result->m_top = m_face->glyph->bitmap_top;
result->m_width = m_face->glyph->advance.x>>6;
result->m_height = m_face->glyph->bitmap_top;
return result;
}

View File

@ -42,8 +42,14 @@ namespace polygun::renderer {
std::unique_ptr<Surface> m_surface;
unsigned m_x;
unsigned m_y;
unsigned m_advance_x;
unsigned m_top;
unsigned m_width;
unsigned m_height;
};
private:
struct GlyphPositionInfo {
unsigned m_text_line_y;
size_t m_glyph_count;
};
public:
@ -52,8 +58,10 @@ namespace polygun::renderer {
bool load_from_file(const std::string& path);
void render_glyphs(const std::string& text, unsigned size, const glm::vec4ub& color, std::vector<std::unique_ptr<RasterizedGlyph>>& output);
std::unique_ptr<Surface> render_text(const std::vector<std::unique_ptr<RasterizedGlyph>>& glyphs);
void render_glyphs_in_area(const glm::vec2& area_size, const std::string& text, unsigned size, const glm::vec4ub& color, std::vector<std::unique_ptr<RasterizedGlyph>>& output);
std::unique_ptr<Surface> render_text(const std::vector<std::unique_ptr<RasterizedGlyph>>& glyphs, const glm::vec2& final_size = glm::vec2(0, 0));
std::unique_ptr<Surface> render_text(const std::string& text, unsigned size, const glm::vec4ub& color);
std::unique_ptr<Surface> render_text_in_area(const glm::vec2& area_size, const std::string& text, unsigned size, const glm::vec4ub& color);
private:
FT_Face m_face;