diff --git a/src/TextRenderer.cpp b/src/TextRenderer.cpp index e924780..24ba392 100644 --- a/src/TextRenderer.cpp +++ b/src/TextRenderer.cpp @@ -31,11 +31,11 @@ #include #include FT_FREETYPE_H #include FT_GLYPH_H -#include #include +#include -#include #include +#include namespace { @@ -81,14 +81,12 @@ auto TextRenderer::flush_font(FontRuntime &rt, FontData &fd) -> void UpdateTexture(fd.atlas, fd.atlas_img.data); } -auto TextRenderer::allocate_region( - FontRuntime &rt, FontData &fd, int width, int height) - -> std::optional> +auto TextRenderer::allocate_region(FontRuntime &rt, FontData &fd, int width, + int height) -> std::optional> { (void)fd; int padded_w = width + kAtlasPadding; - if (padded_w > rt.atlas_width - || height + kAtlasPadding > rt.atlas_height) + if (padded_w > rt.atlas_width || height + kAtlasPadding > rt.atlas_height) return std::nullopt; if (rt.pen_x + padded_w > rt.atlas_width) { rt.pen_x = kAtlasPadding; @@ -108,7 +106,7 @@ auto TextRenderer::upload_region(FontData &fd, int dst_x, int dst_y, int width, int height, std::vector const &buffer) -> void { Rectangle rec { static_cast(dst_x), static_cast(dst_y), - static_cast(width), static_cast(height) }; + static_cast(width), static_cast(height) }; if (fd.atlas.id != 0) UpdateTextureRec(fd.atlas, rec, buffer.data()); if (!fd.atlas_img.data) @@ -129,7 +127,7 @@ auto TextRenderer::generate_glyph(FontRuntime &rt, FontData &fd, msdfgen::GlyphIndex const index(glyph_index); if (!rt.msdf_font || !msdfgen::loadGlyph(shape, rt.msdf_font, index, - msdfgen::FONT_SCALING_EM_NORMALIZED, &advance_em)) + msdfgen::FONT_SCALING_EM_NORMALIZED, &advance_em)) return std::nullopt; shape.normalize(); msdfgen::edgeColoringSimple(shape, 3.0); @@ -137,16 +135,15 @@ auto TextRenderer::generate_glyph(FontRuntime &rt, FontData &fd, float const width_em = static_cast(bounds.r - bounds.l); float const height_em = static_cast(bounds.t - bounds.b); double const scale = rt.em_scale; - int bmp_w = std::max(1, static_cast(std::ceil( - width_em * scale + 2.0 * rt.px_range))); - int bmp_h = std::max(1, static_cast(std::ceil( - height_em * scale + 2.0 * rt.px_range))); + int bmp_w = std::max( + 1, static_cast(std::ceil(width_em * scale + 2.0 * rt.px_range))); + int bmp_h = std::max( + 1, static_cast(std::ceil(height_em * scale + 2.0 * rt.px_range))); if (bmp_w + kAtlasPadding > rt.atlas_width || bmp_h + kAtlasPadding > rt.atlas_height) { - TraceLog(LOG_WARNING, - "Glyph %u bitmap %dx%d exceeds atlas %dx%d", glyph_index, bmp_w, - bmp_h, rt.atlas_width, rt.atlas_height); + TraceLog(LOG_WARNING, "Glyph %u bitmap %dx%d exceeds atlas %dx%d", + glyph_index, bmp_w, bmp_h, rt.atlas_width, rt.atlas_height); GlyphCacheEntry too_large {}; too_large.width = 0; too_large.height = 0; @@ -165,21 +162,24 @@ auto TextRenderer::generate_glyph(FontRuntime &rt, FontData &fd, msdfgen::Bitmap msdf_bitmap(bmp_w, bmp_h); msdfgen::Vector2 scale_vec(scale, scale); double const inv_scale = 1.0 / scale; - msdfgen::Vector2 translate( - -bounds.l + rt.px_range * inv_scale, + msdfgen::Vector2 translate(-bounds.l + rt.px_range * inv_scale, -bounds.b + rt.px_range * inv_scale); - msdfgen::generateMSDF(msdf_bitmap, shape, rt.px_range, scale_vec, translate); + msdfgen::generateMSDF( + msdf_bitmap, shape, rt.px_range, scale_vec, translate); std::vector buffer(static_cast(bmp_w) * bmp_h); for (int y = 0; y < bmp_h; ++y) { + int const dst_y = bmp_h - 1 - y; for (int x = 0; x < bmp_w; ++x) { float const *px = msdf_bitmap(x, y); auto const clamp = [](float v) { + printf("%.2f ", v); return static_cast( std::lround(std::clamp(v, 0.0f, 1.0f) * 255.0f)); }; - buffer[static_cast(y) * bmp_w + x] + buffer[static_cast(dst_y) * bmp_w + x] = Color { clamp(px[0]), clamp(px[1]), clamp(px[2]), 255 }; + printf("\n"); } } @@ -205,11 +205,11 @@ auto TextRenderer::generate_glyph(FontRuntime &rt, FontData &fd, auto const gen_end = std::chrono::steady_clock::now(); auto const gen_ms - = std::chrono::duration(gen_end - gen_start).count(); + = std::chrono::duration(gen_end - gen_start) + .count(); if (gen_ms > 2.0) - TraceLog(LOG_INFO, - "Generated glyph %u in %.2f ms (%dx%d texels)", glyph_index, gen_ms, - entry.width, entry.height); + TraceLog(LOG_INFO, "Generated glyph %u in %.2f ms (%dx%d texels)", + glyph_index, gen_ms, entry.width, entry.height); return entry; } @@ -229,7 +229,8 @@ auto TextRenderer::ensure_glyph(FontRuntime &rt, FontData &fd, u32 glyph_index, = rt.glyph_cache.emplace(glyph_index, std::move(*entry)); if (!ok) return nullptr; - inserted_it->second.stamp = mark_usage ? rt.frame_stamp : inserted_it->second.stamp; + inserted_it->second.stamp + = mark_usage ? rt.frame_stamp : inserted_it->second.stamp; fd.glyphs[glyph_index] = inserted_it->second.glyph; return &inserted_it->second; } @@ -289,9 +290,12 @@ auto TextRenderer::measure_text(FontHandle const font, auto *entry = ensure_glyph(rt, fd, glyph_index, false); if (!entry || entry->width == 0 || entry->height == 0) continue; - float const x_offset_em = hb_to_em(positions[i].x_offset, rt.units_per_em); - float const left = advance_em + x_offset_em + entry->glyph.plane_bounds.left; - float const right = advance_em + x_offset_em + entry->glyph.plane_bounds.right; + float const x_offset_em + = hb_to_em(positions[i].x_offset, rt.units_per_em); + float const left + = advance_em + x_offset_em + entry->glyph.plane_bounds.left; + float const right + = advance_em + x_offset_em + entry->glyph.plane_bounds.right; if (first) { min_x_em = left; max_x_em = right; @@ -306,12 +310,13 @@ auto TextRenderer::measure_text(FontHandle const font, hb_buffer_destroy(buffer); if (first) - return Vector2 { 0.0f, (rt.ascent - rt.descent) * static_cast(size) }; + return Vector2 { 0.0f, + (rt.ascent - rt.descent) * static_cast(size) }; float width_em = std::max(max_x_em, advance_em) - min_x_em; float height_em = rt.ascent - rt.descent; return Vector2 { width_em * static_cast(size), - height_em * static_cast(size) }; + height_em * static_cast(size) }; } auto TextRenderer::draw_text(FontHandle const font, std::string_view const text, @@ -343,12 +348,12 @@ auto TextRenderer::draw_text(FontHandle const font, std::string_view const text, rt.frame_stamp++; // BeginShaderMode(m_msdf_shader); - // if (m_px_range_uniform >= 0) { - // float shader_px_range = rt.px_range; - // SetShaderValue( - // m_msdf_shader, m_px_range_uniform, &shader_px_range, - // SHADER_UNIFORM_FLOAT); - // } + if (m_px_range_uniform >= 0) { + float shader_px_range = rt.px_range; + // SetShaderValue( + // m_msdf_shader, m_px_range_uniform, &shader_px_range, + // SHADER_UNIFORM_FLOAT); + } for (unsigned i = 0; i < length; ++i) { u32 glyph_index = infos[i].codepoint; @@ -359,9 +364,12 @@ auto TextRenderer::draw_text(FontHandle const font, std::string_view const text, continue; if (entry->width == 0 || entry->height == 0) continue; - float const advance_em = hb_to_em(positions[i].x_advance, rt.units_per_em); - float const x_offset_em = hb_to_em(positions[i].x_offset, rt.units_per_em); - float const y_offset_em = hb_to_em(positions[i].y_offset, rt.units_per_em); + float const advance_em + = hb_to_em(positions[i].x_advance, rt.units_per_em); + float const x_offset_em + = hb_to_em(positions[i].x_offset, rt.units_per_em); + float const y_offset_em + = hb_to_em(positions[i].y_offset, rt.units_per_em); float const x_base_em = pen_x_em + x_offset_em; float const y_base_em = pen_y_em + y_offset_em; float const scale_px = size_f / static_cast(rt.em_scale); @@ -374,14 +382,14 @@ auto TextRenderer::draw_text(FontHandle const font, std::string_view const text, float const dest_h = static_cast(entry->height) * scale_px; Rectangle source { - entry->glyph.glyph_bounds.left, - entry->glyph.glyph_bounds.top, - static_cast(entry->width), - static_cast(entry->height), + entry->glyph.glyph_bounds.left, + entry->glyph.glyph_bounds.top, + static_cast(entry->width), + static_cast(entry->height), }; Rectangle dest { dest_x, dest_y, dest_w, dest_h }; - DrawTexturePro(fd.atlas, source, dest, Vector2 { 0.0f, 0.0f }, 0.0f, - color); + DrawTexturePro( + fd.atlas, source, dest, Vector2 { 0.0f, 0.0f }, 0.0f, color); pen_x_em += advance_em; pen_y_em += hb_to_em(positions[i].y_advance, rt.units_per_em); @@ -392,7 +400,8 @@ auto TextRenderer::draw_text(FontHandle const font, std::string_view const text, auto const draw_end = std::chrono::steady_clock::now(); auto const draw_ms - = std::chrono::duration(draw_end - draw_start).count(); + = std::chrono::duration(draw_end - draw_start) + .count(); if (draw_ms > 5.0) TraceLog(LOG_INFO, "draw_text took %.2f ms for %zu glyphs", draw_ms, static_cast(length)); @@ -439,7 +448,8 @@ auto TextRenderer::load_font(std::filesystem::path const &path) FT_Done_Face(face); return std::nullopt; } - hb_font_set_scale(runtime->hb_font, static_cast(runtime->units_per_em) << 6, + hb_font_set_scale(runtime->hb_font, + static_cast(runtime->units_per_em) << 6, static_cast(runtime->units_per_em) << 6); hb_ft_font_set_funcs(runtime->hb_font); diff --git a/tools/dump_msdf b/tools/dump_msdf deleted file mode 100755 index e592c0c..0000000 Binary files a/tools/dump_msdf and /dev/null differ diff --git a/tools/dump_msdf.cpp b/tools/dump_msdf.cpp deleted file mode 100644 index 69cc25b..0000000 --- a/tools/dump_msdf.cpp +++ /dev/null @@ -1,85 +0,0 @@ -#include -#include -#include -#include -#include - -#include -#include - -static unsigned char clamp_channel(float v) { - if (v <= 0.0f) return 0; - if (v >= 1.0f) return 255; - return static_cast(std::lround(v * 255.0f)); -} - -int main(int argc, char **argv) { - if (argc < 3) { - std::fprintf(stderr, "Usage: %s \n", argv[0]); - return 1; - } - const char *fontPath = argv[1]; - unsigned codepoint = std::strtoul(argv[2], nullptr, 0); - auto *ft = msdfgen::initializeFreetype(); - if (!ft) return 1; - auto *font = msdfgen::loadFont(ft, fontPath); - if (!font) return 1; - msdfgen::Shape shape; - double advance = 0.0; - if (!msdfgen::loadGlyph(shape, font, msdfgen::GlyphIndex(codepoint), msdfgen::FONT_SCALING_EM_NORMALIZED, &advance)) { - std::fprintf(stderr, "Failed to load glyph %u\n", codepoint); - return 1; - } - shape.normalize(); - msdfgen::edgeColoringSimple(shape, 3.0); - auto bounds = shape.getBounds(); - - double emScale = 48.0; - double pxRange = 4.0; - int bmp_w = std::max(1, (int)std::ceil((bounds.r - bounds.l) * emScale + 2.0 * pxRange)); - int bmp_h = std::max(1, (int)std::ceil((bounds.t - bounds.b) * emScale + 2.0 * pxRange)); - - msdfgen::Bitmap bitmap(bmp_w, bmp_h); - msdfgen::Vector2 scale(emScale, emScale); - msdfgen::Vector2 translate(-bounds.l * emScale + pxRange, -bounds.b * emScale + pxRange); - msdfgen::generateMSDF(bitmap, shape, pxRange, scale, translate); - - std::vector buffer(static_cast(bmp_w) * bmp_h * 4); - for (int y = 0; y < bmp_h; ++y) { - int dst_y = bmp_h - 1 - y; - for (int x = 0; x < bmp_w; ++x) { - const float *px = bitmap(x, y); - size_t idx = (static_cast(dst_y) * bmp_w + x) * 4; - buffer[idx + 0] = clamp_channel(px[0]); - buffer[idx + 1] = clamp_channel(px[1]); - buffer[idx + 2] = clamp_channel(px[2]); - buffer[idx + 3] = 255; - } - } - - int minX = bmp_w, minY = bmp_h, maxX = -1, maxY = -1; - for (int y = 0; y < bmp_h; ++y) { - for (int x = 0; x < bmp_w; ++x) { - size_t idx = (static_cast(y) * bmp_w + x) * 4; - unsigned char r = buffer[idx]; - unsigned char g = buffer[idx + 1]; - unsigned char b = buffer[idx + 2]; - bool interesting = !(r == 0 && g == 0 && b == 0) && !(r == 255 && g == 255 && b == 255); - if (interesting) { - if (x < minX) minX = x; - if (x > maxX) maxX = x; - if (y < minY) minY = y; - if (y > maxY) maxY = y; - } - } - } - - std::printf("bmp %dx%d\n", bmp_w, bmp_h); - if (maxX >= minX && maxY >= minY) { - std::printf("interesting bbox: x=[%d,%d] y=[%d,%d]\n", minX, maxX, minY, maxY); - } - - msdfgen::destroyFont(font); - msdfgen::deinitializeFreetype(ft); - return 0; -}