diff --git a/src/TextRenderer.cpp b/src/TextRenderer.cpp index 7eb446e..e924780 100644 --- a/src/TextRenderer.cpp +++ b/src/TextRenderer.cpp @@ -164,8 +164,10 @@ 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 * scale + rt.px_range, -bounds.b * scale + rt.px_range); + -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); std::vector buffer(static_cast(bmp_w) * bmp_h); diff --git a/tools/dump_msdf b/tools/dump_msdf new file mode 100755 index 0000000..e592c0c Binary files /dev/null and b/tools/dump_msdf differ diff --git a/tools/dump_msdf.cpp b/tools/dump_msdf.cpp new file mode 100644 index 0000000..69cc25b --- /dev/null +++ b/tools/dump_msdf.cpp @@ -0,0 +1,85 @@ +#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; +}