diff --git a/src/coders/vector_fonts.cpp b/src/coders/vector_fonts.cpp index f838237f7..076067662 100644 --- a/src/coders/vector_fonts.cpp +++ b/src/coders/vector_fonts.cpp @@ -82,6 +82,8 @@ namespace { bool renderGlyph(int codepoint, ImageData& bitmapDst) { int width = bitmapDst.getWidth(); + int height = bitmapDst.getHeight(); + if (FT_Error error = FT_Load_Char(face, codepoint, FT_LOAD_RENDER)) { logger.warning() << get_ft_error_message(error); return false; @@ -89,8 +91,8 @@ namespace { const FT_Bitmap& bitmap = face->glyph->bitmap; auto dstData = bitmapDst.getData(); std::memset(dstData, 0, bitmapDst.getDataSize()); - for (int row = 0; row < bitmap.rows; row++) { - for (int col = 0; col < bitmap.width; col++) { + for (int row = 0; row < std::min(bitmap.rows, height); row++) { + for (int col = 0; col < std::min(bitmap.width, width); col++) { uint8_t value = bitmap.buffer[row * bitmap.pitch + col]; dstData[(row * width + col) * 4 + 0] = 255; dstData[(row * width + col) * 4 + 1] = 255; diff --git a/src/graphics/core/Font.cpp b/src/graphics/core/Font.cpp index d9be091a7..2b37d0db7 100644 --- a/src/graphics/core/Font.cpp +++ b/src/graphics/core/Font.cpp @@ -18,8 +18,8 @@ Font::Font( int yoffset ) : lineHeight(lineHeight), - glyphInterval(lineHeight / 2), yoffset(yoffset), + glyphInterval(lineHeight / 2), pages(std::move(pages)), glyphs(std::move(glyphs)) { } @@ -48,7 +48,22 @@ bool Font::isPrintableChar(uint codepoint) const { } int FontMetrics::calcWidth(std::wstring_view text, size_t offset, size_t length) const { - return std::min(text.length() - offset, length) * _glyphInterval; + auto font = this->font.has_value() ? this->font->lock() : nullptr; + if (font == nullptr) { + return std::min(text.length() - offset, length) * _glyphInterval; + } + int totalWidth = 0; + for (int i = offset; i < offset + length && i < text.length(); i++) { + auto codepoint = text[i]; + if (!font->isPrintableChar(codepoint)) { + totalWidth += _glyphInterval; + } else if (auto glyph = font->getGlyph(codepoint)) { + totalWidth += glyph->xAdvance; + } else { + totalWidth += _glyphInterval; + } + } + return totalWidth; } int Font::calcWidth(std::wstring_view text, size_t length) const { @@ -109,7 +124,6 @@ static inline void draw_glyph( } else { color = style.color; } - batch.sprite( pos + right * (offset.x + i) + up * offset.y, @@ -143,10 +157,12 @@ static inline void draw_text( uint page = 0; uint next = MAX_CODEPAGES; - int x = 0; + float x = 0; int y = 0; bool hasLines = false; + float baseAdvance = glm::length(right); + do { for (size_t i = 0; i < text.length(); i++) { uint c = text[i]; @@ -162,8 +178,10 @@ static inline void draw_text( continue; } int yOffset = 0; + float advance = baseAdvance; if (auto glyph = font.getGlyph(c)) { yOffset = glyph->yOffset; + advance = glyph->xAdvance; } uint charpage = c >> 8; if (charpage == page){ @@ -175,7 +193,7 @@ static inline void draw_text( else if (charpage > page && charpage < next){ next = charpage; } - x++; + x += advance / baseAdvance; } page = next; next = MAX_CODEPAGES; @@ -263,6 +281,10 @@ std::unique_ptr Font::createBitmapFont( int res = pages.at(0)->getHeight() / 16; std::vector> textures; std::vector glyphs(textures.size() * 256); + for (auto& glyph : glyphs) { + glyph.yOffset = 0; + glyph.xAdvance = res / 2; + } for (auto& page : pages) { if (page == nullptr) { textures.emplace_back(nullptr); diff --git a/src/graphics/core/Font.hpp b/src/graphics/core/Font.hpp index 697aae7b6..244e2e8a3 100644 --- a/src/graphics/core/Font.hpp +++ b/src/graphics/core/Font.hpp @@ -76,9 +76,9 @@ public: const Texture* getPage(int page) const; FontMetrics getMetrics() const { - return {lineHeight, yoffset, glyphInterval}; + return {std::nullopt, lineHeight, yoffset, glyphInterval}; } - + const Glyph* getGlyph(int codepoint) const { return (codepoint < 0 || codepoint >= glyphs.size()) ? nullptr diff --git a/src/graphics/core/FontMetics.hpp b/src/graphics/core/FontMetics.hpp index d61864499..a835a495b 100644 --- a/src/graphics/core/FontMetics.hpp +++ b/src/graphics/core/FontMetics.hpp @@ -1,11 +1,19 @@ #pragma once +#include #include +#include + +class Font; struct FontMetrics { + std::optional> font; + int lineHeight; int yoffset; int _glyphInterval = 8; + ~FontMetrics(); + int calcWidth(std::wstring_view text, size_t offset=0, size_t length=-1) const; }; diff --git a/src/graphics/core/FontMetrics.cpp b/src/graphics/core/FontMetrics.cpp new file mode 100644 index 000000000..4727faed0 --- /dev/null +++ b/src/graphics/core/FontMetrics.cpp @@ -0,0 +1,5 @@ +#include "FontMetics.hpp" + +#include "Font.hpp" + +FontMetrics::~FontMetrics() = default; diff --git a/src/graphics/ui/elements/Label.cpp b/src/graphics/ui/elements/Label.cpp index 374c088a0..9387e91c5 100644 --- a/src/graphics/ui/elements/Label.cpp +++ b/src/graphics/ui/elements/Label.cpp @@ -11,11 +11,11 @@ using namespace gui; -void LabelCache::prepare(std::ptrdiff_t fontId, FontMetrics metrics, size_t wrapWidth) { - if (fontId != this->fontId) { +void LabelCache::prepare(const std::shared_ptr& font, FontMetrics metrics, size_t wrapWidth) { + if (!this->metrics.font.has_value() || font.get() != this->metrics.font.value().lock().get()) { resetFlag = true; - this->fontId = fontId; this->metrics = metrics; + this->metrics.font = font; } if (wrapWidth != this->wrapWidth) { resetFlag = true; @@ -42,7 +42,8 @@ void LabelCache::update(std::wstring_view text, bool multiline, bool wrap) { lines.clear(); lines.push_back(LineScheme {0, false}); - if (fontId == 0) { + auto font = metrics.font.has_value() ? metrics.font.value().lock() : nullptr; + if (font == nullptr) { wrap = false; } @@ -61,26 +62,27 @@ void LabelCache::update(std::wstring_view text, bool multiline, bool wrap) { } } } - if (fontId != 0) { - int maxWidth = 0; - for (int i = 0; i < lines.size() - 1; i++) { - const auto& next = lines[i + 1]; - const auto& cur = lines[i]; - maxWidth = std::max( - metrics.calcWidth( - text.substr(cur.offset, next.offset - cur.offset) - ), - maxWidth - ); - } + if (font == nullptr) { + return; + } + int maxWidth = 0; + for (int i = 0; i < lines.size() - 1; i++) { + const auto& next = lines[i + 1]; + const auto& cur = lines[i]; maxWidth = std::max( metrics.calcWidth( - text.substr(lines[lines.size() - 1].offset) + text.substr(cur.offset, next.offset - cur.offset) ), maxWidth ); - multilineWidth = maxWidth; } + maxWidth = std::max( + metrics.calcWidth( + text.substr(lines[lines.size() - 1].offset) + ), + maxWidth + ); + multilineWidth = maxWidth; } } @@ -136,7 +138,7 @@ void Label::setText(std::wstring text) { this->text = std::move(text); cache.update(this->text, multiline, textWrap); - if (cache.fontId != 0 && autoresize) { + if (cache.metrics.font.has_value() && !cache.metrics.font->expired() && autoresize) { setSize(calcSize()); } } @@ -203,9 +205,9 @@ uint Label::getLinesNumber() const { void Label::draw(const DrawContext& pctx, const Assets& assets) { auto batch = pctx.getBatch2D(); - auto font = assets.get(fontName); + auto font = assets.getShared(fontName); cache.prepare( - reinterpret_cast(font), + font, font->getMetrics(), static_cast(glm::abs(getSize().x)) ); diff --git a/src/graphics/ui/elements/Label.hpp b/src/graphics/ui/elements/Label.hpp index 07f9e91ce..aace3ba94 100644 --- a/src/graphics/ui/elements/Label.hpp +++ b/src/graphics/ui/elements/Label.hpp @@ -15,7 +15,6 @@ namespace gui { }; struct LabelCache { - ptrdiff_t fontId = 0; FontMetrics metrics; std::vector lines; @@ -24,7 +23,7 @@ namespace gui { size_t wrapWidth = -1; int multilineWidth = 0; - void prepare(std::ptrdiff_t fontId, FontMetrics metrics, size_t wrapWidth); + void prepare(const std::shared_ptr& font, FontMetrics metrics, size_t wrapWidth); void update(std::wstring_view text, bool multiline, bool wrap); size_t getTextLineOffset(size_t line) const; diff --git a/src/graphics/ui/elements/TextBox.cpp b/src/graphics/ui/elements/TextBox.cpp index 26160e126..fb8dbd9c8 100644 --- a/src/graphics/ui/elements/TextBox.cpp +++ b/src/graphics/ui/elements/TextBox.cpp @@ -252,10 +252,12 @@ void TextBox::draw(const DrawContext& pctx, const Assets& assets) { float time = gui.getWindow().time(); + // drawing caret here if (isFocused() && editable && static_cast((time - caretLastMove) * 2) % 2 == 0) { uint line = label->getLineByTextIndex(caret); + auto linestart = label->getTextLineOffset(line); uint lcaret = caret - label->getTextLineOffset(line); - int width = rawTextCache.metrics.calcWidth(input, 0, lcaret); + int width = rawTextCache.metrics.calcWidth(input.substr(linestart), 0, lcaret); batch->rect( lcoord.x + width, @@ -264,6 +266,7 @@ void TextBox::draw(const DrawContext& pctx, const Assets& assets) { lineHeight ); } + // drawing selection if (selectionStart != selectionEnd) { auto selectionCtx = subctx.sub(batch); selectionCtx.setBlendMode(BlendMode::addition); @@ -349,12 +352,8 @@ void TextBox::draw(const DrawContext& pctx, const Assets& assets) { } void TextBox::drawBackground(const DrawContext& pctx, const Assets& assets) { - auto font = assets.get(label->getFontName()); - rawTextCache.prepare( - reinterpret_cast(font), - font->getMetrics(), - label->getSize().x - ); + auto font = assets.getShared(label->getFontName()); + rawTextCache.prepare(font, font->getMetrics(), label->getSize().x); glm::vec2 pos = calcPos(); @@ -384,8 +383,11 @@ void TextBox::drawBackground(const DrawContext& pctx, const Assets& assets) { } void TextBox::refreshLabel() { + if (!rawTextCache.metrics.font.has_value()) { + return; + } rawTextCache.prepare( - rawTextCache.fontId, + rawTextCache.metrics.font.value().lock(), rawTextCache.metrics, static_cast(getSize().x) ); @@ -425,7 +427,7 @@ void TextBox::refreshLabel() { lineNumbersLabel->setColor(glm::vec4(1, 1, 1, 0.25f)); } - if (autoresize && rawTextCache.fontId) { + if (autoresize) { auto size = getSize(); int newy = glm::min( static_cast(parent->getSize().y), @@ -443,7 +445,7 @@ void TextBox::refreshLabel() { } } - if (multiline && rawTextCache.fontId) { + if (multiline) { setScrollable(true); uint height = label->getLinesNumber() * rawTextCache.metrics.lineHeight * label->getLineInterval(); @@ -665,7 +667,7 @@ size_t TextBox::normalizeIndex(int index) { /// @param y screen Y position /// @return non-normalized character index int TextBox::calcIndexAt(int x, int y) const { - if (rawTextCache.fontId == 0) return 0; + if (!rawTextCache.metrics.font.has_value()) return 0; const auto& labelText = label->getText(); glm::vec2 lcoord = label->calcPos(); uint line = label->getLineByYOffset(y - lcoord.y); @@ -682,7 +684,7 @@ int TextBox::calcIndexAt(int x, int y) const { } int TextBox::getLineYOffset(int line) const { - if (rawTextCache.fontId == 0) return 0; + if (!rawTextCache.metrics.font.has_value()) return 0; return label->getLineYOffset(line); } @@ -1205,12 +1207,16 @@ size_t TextBox::getCaret() const { void TextBox::setCaret(size_t position) { const auto& labelText = label->getText(); caret = std::min(static_cast(position), input.length()); - if (rawTextCache.fontId == 0) { + + auto font = rawTextCache.metrics.font.has_value() + ? rawTextCache.metrics.font->lock() + : nullptr; + if (font == nullptr) { return; } int width = label->getSize().x; - rawTextCache.prepare(rawTextCache.fontId, rawTextCache.metrics, width); + rawTextCache.prepare(font, rawTextCache.metrics, width); rawTextCache.update(input, multiline, label->isTextWrapping()); caretLastMove = gui.getWindow().time();