You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Thank you very much for your hard work on msdfgen; it's really impressive.
I've been working on integrating it into my text rendering pipeline, which currently uses FreeType to generate font atlases, Harfbuzz for layout, and OpenGL or Metal for rendering the text.
I've got most of the way there, thanks to @Haeri's code in #117. However, I'm struggling to work out how exactly to size the bounding boxes for the glyphs in a way that lines up with the coordinates produced by Harfbuzz. Here is an example of text being rendered with a plain rasterised font atlas, at 32px size:
And here is the same text being rendered via an atlas created using msdfgen:
We can see that the glyphs themselves look pretty good, but some of the sizes (and possibly positions, unsure) are a little off.
My question is: is this a flaw with how I am using msdfgen to compute the bounds of glyphs when rendering them into the atlas, or is msdfgen's bounds calculation different to FreeType's in a way that means I need to dig deeper into the internal workings to get this matched up?
My code is as follows:
Result<FontAtlas> Growl::createDistanceFieldFontAtlasFromFont(Font& font) noexcept {
msdfgen::FontHandle* font_handle = msdfgen::adoptFreetypeFont(font.getFTFontData().face);
msdfgen::FontMetrics font_metrics;
msdfgen::getFontMetrics(font_metrics, font_handle);
float scale = 32 / font_metrics.emSize;
int border = 2;
int pack_border = 1;
float range = 2;
std::vector<stbrp_rect> glyph_rects;
for (int i = 0; i < font.getFTFontData().face->num_glyphs; i++) {
msdfgen::Shape shape;
msdfgen::loadGlyph(shape, font_handle, msdfgen::GlyphIndex(i));
if (shape.validate() && shape.contours.size() > 0) {
shape.inverseYAxis = true;
shape.normalize();
shape.orientContours();
auto bounds = shape.getBounds(border);
int glyph_width = round((bounds.r - bounds.l) * scale);
int glyph_height = round((bounds.t - bounds.b) * scale);
glyph_rects.push_back(stbrp_rect{i, glyph_width + (pack_border * 2), glyph_height + (pack_border * 2)});
}
}
int width, height;
// This function just uses stb_rectpack to produce an array of packed rectangles in increasingly sized textures.if (!packRectsIncreasing(glyph_rects, nextPowerOfTwo(std::max(glyph_rects[0].w, glyph_rects[0].h)), &width, &height)) {
returnError(std::make_unique<AssetsError>(
"Failed to pack font in texture; too large"));
}
std::unordered_map<int, GlyphPosition> glyphs;
std::vector<unsignedchar> image_data(width * height * 4, 0);
for (constauto& rect : glyph_rects) {
msdfgen::Shape shape;
msdfgen::loadGlyph(shape, font_handle, msdfgen::GlyphIndex(rect.id));
if (shape.validate() && shape.contours.size() > 0) {
shape.inverseYAxis = true;
shape.normalize();
shape.orientContours();
auto bounds = shape.getBounds(border);
msdfgen::edgeColoringSimple(shape, 3);
msdfgen::Bitmap<float, 3> bitmap(rect.w - (pack_border * 2), rect.h - (pack_border * 2));
msdfgen::generateMSDF(bitmap, shape, range, scale, msdfgen::Vector2(-bounds.l, -bounds.b));
for (int row = 0; row < bitmap.height(); row++) {
for (int col = 0; col < bitmap.width(); col++) {
int x = rect.x + pack_border + col;
int y = rect.y + pack_border + row;
unsignedchar* dst = image_data.data() + 4 * (y * width + x);
*dst++ = msdfgen::pixelFloatToByte(bitmap(col, row)[0]);
*dst++ = msdfgen::pixelFloatToByte(bitmap(col, row)[1]);
*dst++ = msdfgen::pixelFloatToByte(bitmap(col, row)[2]);
*dst++ = 0xFF;
}
}
int border_calc = floor(border * scale);
glyphs[rect.id] = GlyphPosition{rect.x + pack_border + border_calc, rect.y + pack_border + border_calc, rect.w - ((pack_border+border_calc) * 2), rect.h - ((pack_border+border_calc) * 2)};
}
}
fpng::fpng_encode_image_to_file("debug.png", image_data.data(), width, height, 4);
msdfgen::destroyFont(font_handle);
returnFontAtlas(
font, std::make_unique<Image>(width, height, 4, image_data),
std::move(glyphs));
}
Here is the generated atlas:
I suspect I am not using msdf's borders correctly, but even with borders set to 0 the misalignment is present, so it's likely more to do with how I'm using the returned bounds.
reacted with thumbs up emoji reacted with thumbs down emoji reacted with laugh emoji reacted with hooray emoji reacted with confused emoji reacted with heart emoji reacted with rocket emoji reacted with eyes emoji
-
Hi there,
Thank you very much for your hard work on msdfgen; it's really impressive.
I've been working on integrating it into my text rendering pipeline, which currently uses FreeType to generate font atlases, Harfbuzz for layout, and OpenGL or Metal for rendering the text.
I've got most of the way there, thanks to @Haeri's code in #117. However, I'm struggling to work out how exactly to size the bounding boxes for the glyphs in a way that lines up with the coordinates produced by Harfbuzz. Here is an example of text being rendered with a plain rasterised font atlas, at 32px size:
And here is the same text being rendered via an atlas created using msdfgen:
We can see that the glyphs themselves look pretty good, but some of the sizes (and possibly positions, unsure) are a little off.
My question is: is this a flaw with how I am using msdfgen to compute the bounds of glyphs when rendering them into the atlas, or is msdfgen's bounds calculation different to FreeType's in a way that means I need to dig deeper into the internal workings to get this matched up?
My code is as follows:
Here is the generated atlas:
I suspect I am not using msdf's borders correctly, but even with borders set to 0 the misalignment is present, so it's likely more to do with how I'm using the returned bounds.
Thanks again for the great library.
Beta Was this translation helpful? Give feedback.
All reactions