Fix font glyph generation to work correctly with other fonts
This commit is contained in:
parent
069d61e487
commit
d018957b09
BIN
assets/opensb/font/mono-b.ttf
Normal file
BIN
assets/opensb/font/mono-b.ttf
Normal file
Binary file not shown.
BIN
assets/opensb/font/mono-bi.ttf
Normal file
BIN
assets/opensb/font/mono-bi.ttf
Normal file
Binary file not shown.
BIN
assets/opensb/font/mono-i.ttf
Normal file
BIN
assets/opensb/font/mono-i.ttf
Normal file
Binary file not shown.
BIN
assets/opensb/font/mono.ttf
Normal file
BIN
assets/opensb/font/mono.ttf
Normal file
Binary file not shown.
@ -13,7 +13,10 @@
|
|||||||
"cursorTooltip" : {
|
"cursorTooltip" : {
|
||||||
"font" : ""
|
"font" : ""
|
||||||
},
|
},
|
||||||
"debugFont" : "",
|
|
||||||
|
"debugFont" : "mono-b",
|
||||||
|
"debugFontSize" : 6,
|
||||||
|
"debugFontDirectives" : "?border=1;0007;0000",
|
||||||
|
|
||||||
// Change planet name to support the new internal string formatting.
|
// Change planet name to support the new internal string formatting.
|
||||||
"planetNameFormatString" : "- {} -",
|
"planetNameFormatString" : "- {} -",
|
||||||
@ -24,5 +27,5 @@
|
|||||||
"buttonHoverOffSound" : [ "/sfx/interface/button/hover_off.wav" ],
|
"buttonHoverOffSound" : [ "/sfx/interface/button/hover_off.wav" ],
|
||||||
|
|
||||||
"debugSpatialClearTime" : 0.0,
|
"debugSpatialClearTime" : 0.0,
|
||||||
"debugOffset" : [64, 100]
|
"debugOffset" : [80, 80]
|
||||||
}
|
}
|
@ -855,14 +855,15 @@ void ClientApplication::updateRunning() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Vec2F aimPosition = m_player->aimPosition();
|
Vec2F aimPosition = m_player->aimPosition();
|
||||||
LogMap::set("render_fps", appController()->renderFps());
|
float fps = appController()->renderFps();
|
||||||
LogMap::set("update_rate", appController()->updateRate());
|
LogMap::set("render_rate", strf("{:4.2f} FPS ({:4.2f}ms)", fps, (1.0f / appController()->renderFps()) * 1000.0f));
|
||||||
LogMap::set("player_pos", strf("{:4.2f} {:4.2f}", m_player->position()[0], m_player->position()[1]));
|
LogMap::set("update_rate", strf("{:4.2f}Hz", appController()->updateRate()));
|
||||||
LogMap::set("player_vel", strf("{:4.2f} {:4.2f}", m_player->velocity()[0], m_player->velocity()[1]));
|
LogMap::set("player_pos", strf("[ ^#f45;{:4.2f}^reset;, ^#49f;{:4.2f}^reset; ]", m_player->position()[0], m_player->position()[1]));
|
||||||
LogMap::set("player_aim", strf("{:4.2f} {:4.2f}", aimPosition[0], aimPosition[1]));
|
LogMap::set("player_vel", strf("[ ^#f45;{:4.2f}^reset;, ^#49f;{:4.2f}^reset; ]", m_player->velocity()[0], m_player->velocity()[1]));
|
||||||
|
LogMap::set("player_aim", strf("[ ^#f45;{:4.2f}^reset;, ^#49f;{:4.2f}^reset; ]", aimPosition[0], aimPosition[1]));
|
||||||
if (m_universeClient->worldClient()) {
|
if (m_universeClient->worldClient()) {
|
||||||
LogMap::set("liquid_level", strf("{}", m_universeClient->worldClient()->liquidLevel(Vec2I::floor(aimPosition)).level));
|
LogMap::set("liquid_level", strf("{}", m_universeClient->worldClient()->liquidLevel(Vec2I::floor(aimPosition)).level));
|
||||||
LogMap::set("dungeonId", strf("{}", m_universeClient->worldClient()->dungeonId(Vec2I::floor(aimPosition))));
|
LogMap::set("dungeon_id", strf("{}", m_universeClient->worldClient()->dungeonId(Vec2I::floor(aimPosition))));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (m_mainInterface->currentState() == MainInterface::ReturnToTitle)
|
if (m_mainInterface->currentState() == MainInterface::ReturnToTitle)
|
||||||
|
@ -7,6 +7,8 @@
|
|||||||
|
|
||||||
namespace Star {
|
namespace Star {
|
||||||
|
|
||||||
|
constexpr int FontLoadFlags = FT_LOAD_FORCE_AUTOHINT;
|
||||||
|
|
||||||
struct FTContext {
|
struct FTContext {
|
||||||
FT_Library library;
|
FT_Library library;
|
||||||
|
|
||||||
@ -49,9 +51,7 @@ FontPtr Font::loadTrueTypeFont(ByteArrayConstPtr const& bytes, unsigned pixelSiz
|
|||||||
return font;
|
return font;
|
||||||
}
|
}
|
||||||
|
|
||||||
Font::Font() {
|
Font::Font() : m_pixelSize(0) {}
|
||||||
m_pixelSize = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
FontPtr Font::clone() const {
|
FontPtr Font::clone() const {
|
||||||
return Font::loadTrueTypeFont(m_fontBuffer, m_pixelSize);
|
return Font::loadTrueTypeFont(m_fontBuffer, m_pixelSize);
|
||||||
@ -78,38 +78,38 @@ unsigned Font::width(String::Char c) {
|
|||||||
if (auto width = m_widthCache.maybe({c, m_pixelSize})) {
|
if (auto width = m_widthCache.maybe({c, m_pixelSize})) {
|
||||||
return *width;
|
return *width;
|
||||||
} else {
|
} else {
|
||||||
FT_Load_Char(m_fontImpl->face, c, FT_LOAD_FORCE_AUTOHINT);
|
FT_Load_Char(m_fontImpl->face, c, FontLoadFlags);
|
||||||
unsigned newWidth = (m_fontImpl->face->glyph->advance.x + 32) / 64;
|
unsigned newWidth = (m_fontImpl->face->glyph->advance.x + 32) / 64;
|
||||||
m_widthCache.insert({c, m_pixelSize}, newWidth);
|
m_widthCache.insert({c, m_pixelSize}, newWidth);
|
||||||
return newWidth;
|
return newWidth;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Image Font::render(String::Char c) {
|
|
||||||
|
std::pair<Image, int> Font::render(String::Char c) {
|
||||||
if (!m_fontImpl)
|
if (!m_fontImpl)
|
||||||
throw FontException("Font::render called on uninitialized font.");
|
throw FontException("Font::render called on uninitialized font.");
|
||||||
|
|
||||||
FT_UInt glyph_index = FT_Get_Char_Index(m_fontImpl->face, c);
|
FT_Face face = m_fontImpl->face;
|
||||||
if (FT_Load_Glyph(m_fontImpl->face, glyph_index, FT_LOAD_FORCE_AUTOHINT) != 0)
|
FT_UInt glyph_index = FT_Get_Char_Index(face, c);
|
||||||
|
if (FT_Load_Glyph(face, glyph_index, FontLoadFlags) != 0)
|
||||||
return {};
|
return {};
|
||||||
|
|
||||||
/* convert to an anti-aliased bitmap */
|
/* convert to an anti-aliased bitmap */
|
||||||
if (FT_Render_Glyph(m_fontImpl->face->glyph, FT_RENDER_MODE_NORMAL) != 0)
|
if (FT_Render_Glyph(face->glyph, FT_RENDER_MODE_NORMAL) != 0)
|
||||||
return {};
|
return {};
|
||||||
|
|
||||||
FT_GlyphSlot slot = m_fontImpl->face->glyph;
|
FT_GlyphSlot slot = face->glyph;
|
||||||
|
|
||||||
int width = (slot->advance.x + 32) / 64;
|
unsigned int width = slot->bitmap.width;
|
||||||
int height = m_pixelSize;
|
unsigned int height = slot->bitmap.rows;
|
||||||
|
|
||||||
Image image(width, height, PixelFormat::RGBA32);
|
Image image(width, height, PixelFormat::RGBA32);
|
||||||
for (int y = 0; y < height; ++y) {
|
for (int y = 0; y < height; ++y) {
|
||||||
|
unsigned char* p = slot->bitmap.buffer + y * slot->bitmap.pitch;
|
||||||
for (int x = 0; x < width; ++x) {
|
for (int x = 0; x < width; ++x) {
|
||||||
int bx = x;
|
if (x >= 0 && y >= 0 && x < width && y < height) {
|
||||||
int by = y + slot->bitmap_top - m_fontImpl->face->size->metrics.ascender / 64;
|
unsigned char val = *(p + x);
|
||||||
if (bx >= 0 && by >= 0 && bx < (int)slot->bitmap.width && by < (int)slot->bitmap.rows) {
|
|
||||||
unsigned char* p = slot->bitmap.buffer + by * slot->bitmap.pitch;
|
|
||||||
unsigned char val = *(p + bx);
|
|
||||||
image.set(x, height - y - 1, Vec4B(255, 255, 255, val));
|
image.set(x, height - y - 1, Vec4B(255, 255, 255, val));
|
||||||
} else {
|
} else {
|
||||||
image.set(x, height - y - 1, Vec4B(255, 255, 255, 0));
|
image.set(x, height - y - 1, Vec4B(255, 255, 255, 0));
|
||||||
@ -117,7 +117,7 @@ Image Font::render(String::Char c) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return image;
|
return { move(image), (slot->bitmap_top - (int)height) + m_pixelSize / 4 };
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -34,12 +34,13 @@ public:
|
|||||||
// May return empty image on unrenderable character (Normally, this will
|
// May return empty image on unrenderable character (Normally, this will
|
||||||
// render a box, but if there is an internal freetype error this may return
|
// render a box, but if there is an internal freetype error this may return
|
||||||
// an empty image).
|
// an empty image).
|
||||||
Image render(String::Char c);
|
std::pair<Image, int> render(String::Char c);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
FontImplPtr m_fontImpl;
|
FontImplPtr m_fontImpl;
|
||||||
ByteArrayConstPtr m_fontBuffer;
|
ByteArrayConstPtr m_fontBuffer;
|
||||||
unsigned m_pixelSize;
|
unsigned m_pixelSize;
|
||||||
|
|
||||||
HashMap<pair<String::Char, unsigned>, unsigned> m_widthCache;
|
HashMap<pair<String::Char, unsigned>, unsigned> m_widthCache;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -1261,33 +1261,44 @@ void MainInterface::renderDebug() {
|
|||||||
auto assets = Root::singleton().assets();
|
auto assets = Root::singleton().assets();
|
||||||
m_guiContext->setFontSize(m_config->debugFontSize);
|
m_guiContext->setFontSize(m_config->debugFontSize);
|
||||||
m_guiContext->setFont(m_config->debugFont);
|
m_guiContext->setFont(m_config->debugFont);
|
||||||
|
m_guiContext->setFontProcessingDirectives(m_config->debugFontDirectives);
|
||||||
m_guiContext->setFontColor(Color::Green.toRgba());
|
m_guiContext->setFontColor(Color::Green.toRgba());
|
||||||
|
m_guiContext->setFontMode(FontMode::Normal);
|
||||||
|
|
||||||
bool clearMap = m_debugMapClearTimer.wrapTick();
|
bool clearMap = m_debugMapClearTimer.wrapTick();
|
||||||
auto logMapValues = LogMap::getValues();
|
auto logMapValues = LogMap::getValues();
|
||||||
if (clearMap)
|
if (clearMap)
|
||||||
LogMap::clear();
|
LogMap::clear();
|
||||||
|
|
||||||
|
List<String> formatted;
|
||||||
|
formatted.reserve(logMapValues.size());
|
||||||
|
|
||||||
int counter = 0;
|
int counter = 0;
|
||||||
for (auto const& pair : logMapValues) {
|
for (auto const& pair : logMapValues) {
|
||||||
TextPositioning positioning = {Vec2F(m_config->debugOffset[0], windowHeight() - m_config->debugOffset[1] - m_config->fontSize * interfaceScale() * counter)};
|
TextPositioning positioning = {Vec2F(m_config->debugOffset[0], windowHeight() - m_config->debugOffset[1] - m_config->fontSize * interfaceScale() * counter++)};
|
||||||
m_debugTextRect.combine(m_guiContext->determineTextSize(strf("{}: {}", pair.first, pair.second), positioning).padded(m_config->debugBackgroundPad));
|
String& text = formatted.emplace_back(strf("^white;{}^lightgray;:^reset; {}", pair.first, pair.second));
|
||||||
++counter;
|
m_debugTextRect.combine(m_guiContext->determineTextSize(text, positioning).padded(m_config->debugBackgroundPad));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!m_debugTextRect.isNull())
|
if (!m_debugTextRect.isNull()) {
|
||||||
m_guiContext->drawQuad(m_debugTextRect, m_config->debugBackgroundColor.toRgba());
|
RenderQuad& quad = m_guiContext->renderer()->immediatePrimitives()
|
||||||
|
.emplace_back(std::in_place_type_t<RenderQuad>(), m_debugTextRect, m_config->debugBackgroundColor.toRgba(), 0.0f).get<RenderQuad>();
|
||||||
|
|
||||||
|
quad.b.color[3] = quad.c.color[3] = 0;
|
||||||
|
};
|
||||||
|
|
||||||
if (clearMap)
|
|
||||||
m_debugTextRect = RectF::null();
|
m_debugTextRect = RectF::null();
|
||||||
|
|
||||||
counter = 0;
|
counter = 0;
|
||||||
for (auto const& pair : logMapValues) {
|
for (auto const& pair : logMapValues) {
|
||||||
TextPositioning positioning = {Vec2F(m_config->debugOffset[0], windowHeight() - m_config->debugOffset[1] - m_config->fontSize * interfaceScale() * counter)};
|
TextPositioning positioning = {Vec2F(m_config->debugOffset[0], windowHeight() - m_config->debugOffset[1] - m_config->fontSize * interfaceScale() * counter)};
|
||||||
m_guiContext->renderText(strf("{}: {}", pair.first, pair.second), positioning);
|
m_guiContext->renderText(formatted[counter], positioning);
|
||||||
++counter;
|
++counter;
|
||||||
}
|
}
|
||||||
|
m_guiContext->setFontSize(8);
|
||||||
|
m_guiContext->setDefaultFont();
|
||||||
m_guiContext->setFontColor(Vec4B::filled(255));
|
m_guiContext->setFontColor(Vec4B::filled(255));
|
||||||
|
m_guiContext->setFontProcessingDirectives("");
|
||||||
|
|
||||||
auto const& camera = m_worldPainter->camera();
|
auto const& camera = m_worldPainter->camera();
|
||||||
|
|
||||||
|
@ -114,6 +114,7 @@ MainInterfaceConfigPtr MainInterfaceConfig::loadFromAssets() {
|
|||||||
config->debugOffset = jsonToVec2I(assets->json("/interface.config:debugOffset"));
|
config->debugOffset = jsonToVec2I(assets->json("/interface.config:debugOffset"));
|
||||||
config->debugFontSize = assets->json("/interface.config:debugFontSize").toUInt();
|
config->debugFontSize = assets->json("/interface.config:debugFontSize").toUInt();
|
||||||
config->debugFont = assets->json("/interface.config:debugFont").toString();
|
config->debugFont = assets->json("/interface.config:debugFont").toString();
|
||||||
|
config->debugFontDirectives = assets->json("/interface.config:debugFontDirectives").toString();
|
||||||
config->debugSpatialClearTime = assets->json("/interface.config:debugSpatialClearTime").toFloat();
|
config->debugSpatialClearTime = assets->json("/interface.config:debugSpatialClearTime").toFloat();
|
||||||
config->debugMapClearTime = assets->json("/interface.config:debugMapClearTime").toFloat();
|
config->debugMapClearTime = assets->json("/interface.config:debugMapClearTime").toFloat();
|
||||||
config->debugBackgroundColor = jsonToColor(assets->json("/interface.config:debugBackgroundColor"));
|
config->debugBackgroundColor = jsonToColor(assets->json("/interface.config:debugBackgroundColor"));
|
||||||
|
@ -145,6 +145,7 @@ struct MainInterfaceConfig {
|
|||||||
Vec2I debugOffset;
|
Vec2I debugOffset;
|
||||||
unsigned debugFontSize;
|
unsigned debugFontSize;
|
||||||
String debugFont;
|
String debugFont;
|
||||||
|
String debugFontDirectives;
|
||||||
float debugSpatialClearTime;
|
float debugSpatialClearTime;
|
||||||
float debugMapClearTime;
|
float debugMapClearTime;
|
||||||
Color debugBackgroundColor;
|
Color debugBackgroundColor;
|
||||||
|
@ -45,7 +45,8 @@ const FontTextureGroup::GlyphTexture& FontTextureGroup::glyphTexture(String::Cha
|
|||||||
|
|
||||||
if (res.second) {
|
if (res.second) {
|
||||||
m_font->setPixelSize(size);
|
m_font->setPixelSize(size);
|
||||||
Image image = m_font->render(c);
|
auto pair = m_font->render(c);
|
||||||
|
Image& image = pair.first;
|
||||||
if (!processingDirectives.empty()) {
|
if (!processingDirectives.empty()) {
|
||||||
try {
|
try {
|
||||||
Vec2F preSize = Vec2F(image.size());
|
Vec2F preSize = Vec2F(image.size());
|
||||||
@ -55,7 +56,7 @@ const FontTextureGroup::GlyphTexture& FontTextureGroup::glyphTexture(String::Cha
|
|||||||
border->includeTransparent = true;
|
border->includeTransparent = true;
|
||||||
}
|
}
|
||||||
image = processImageOperations(imageOperations, image);
|
image = processImageOperations(imageOperations, image);
|
||||||
res.first->second.processingOffset = preSize - Vec2F(image.size());
|
res.first->second.offset = (preSize - Vec2F(image.size())) / 2;
|
||||||
}
|
}
|
||||||
catch (StarException&) {
|
catch (StarException&) {
|
||||||
image.forEachPixel([](unsigned x, unsigned y, Vec4B& pixel) {
|
image.forEachPixel([](unsigned x, unsigned y, Vec4B& pixel) {
|
||||||
@ -64,8 +65,9 @@ const FontTextureGroup::GlyphTexture& FontTextureGroup::glyphTexture(String::Cha
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
res.first->second.processingOffset = Vec2F();
|
res.first->second.offset = Vec2F();
|
||||||
|
|
||||||
|
res.first->second.offset[1] += pair.second;
|
||||||
res.first->second.texture = m_textureGroup->create(image);
|
res.first->second.texture = m_textureGroup->create(image);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -17,7 +17,7 @@ public:
|
|||||||
struct GlyphTexture {
|
struct GlyphTexture {
|
||||||
TexturePtr texture;
|
TexturePtr texture;
|
||||||
int64_t time;
|
int64_t time;
|
||||||
Vec2F processingOffset;
|
Vec2F offset;
|
||||||
};
|
};
|
||||||
|
|
||||||
FontTextureGroup(TextureGroupPtr textureGroup);
|
FontTextureGroup(TextureGroupPtr textureGroup);
|
||||||
|
@ -586,7 +586,7 @@ void TextPainter::renderGlyph(String::Char c, Vec2F const& screenPos, unsigned f
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
const FontTextureGroup::GlyphTexture& glyphTexture = m_fontTextureGroup.glyphTexture(c, fontSize, processingDirectives);
|
const FontTextureGroup::GlyphTexture& glyphTexture = m_fontTextureGroup.glyphTexture(c, fontSize, processingDirectives);
|
||||||
Vec2F offset = glyphTexture.processingOffset * (scale * 0.5f);
|
Vec2F offset = glyphTexture.offset * scale;
|
||||||
m_renderer->immediatePrimitives().emplace_back(std::in_place_type_t<RenderQuad>(), glyphTexture.texture, screenPos + offset, scale, color, 0.0f);
|
m_renderer->immediatePrimitives().emplace_back(std::in_place_type_t<RenderQuad>(), glyphTexture.texture, screenPos + offset, scale, color, 0.0f);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user