diff options
author | Kae <80987908+Novaenia@users.noreply.github.com> | 2024-04-22 06:07:59 +1000 |
---|---|---|
committer | Kae <80987908+Novaenia@users.noreply.github.com> | 2024-04-22 06:07:59 +1000 |
commit | ca1426eabc873f781eb0dd389d45634b7d183250 (patch) | |
tree | 15ea83658ca3824232f14fe4b32ec714e0aa05c6 /source/rendering | |
parent | d5f5fb5ddf0d4a9f0b0e6ac012121926d2fcd949 (diff) |
Lua chat callbacks + better font styling
golly gee whiz!! i hope i didn't fuck something up
Diffstat (limited to 'source/rendering')
-rw-r--r-- | source/rendering/StarFontTextureGroup.cpp | 5 | ||||
-rw-r--r-- | source/rendering/StarTextPainter.cpp | 208 | ||||
-rw-r--r-- | source/rendering/StarTextPainter.hpp | 43 |
3 files changed, 140 insertions, 116 deletions
diff --git a/source/rendering/StarFontTextureGroup.cpp b/source/rendering/StarFontTextureGroup.cpp index e8f701a..fb3a107 100644 --- a/source/rendering/StarFontTextureGroup.cpp +++ b/source/rendering/StarFontTextureGroup.cpp @@ -47,11 +47,12 @@ const FontTextureGroup::GlyphTexture& FontTextureGroup::glyphTexture(String::Cha m_font->setPixelSize(size); auto pair = m_font->render(c); Image& image = pair.first; - if (processingDirectives && *processingDirectives) { + if (processingDirectives) { try { + Directives const& directives = *processingDirectives; Vec2F preSize = Vec2F(image.size()); - for (auto& entry : processingDirectives->shared->entries) + for (auto& entry : directives->entries) processImageOperation(entry.operation, image); res.first->second.offset = (preSize - Vec2F(image.size())) / 2; diff --git a/source/rendering/StarTextPainter.cpp b/source/rendering/StarTextPainter.cpp index 8e21488..8198969 100644 --- a/source/rendering/StarTextPainter.cpp +++ b/source/rendering/StarTextPainter.cpp @@ -1,7 +1,5 @@ #include "StarTextPainter.hpp" #include "StarJsonExtra.hpp" -#include "StarText.hpp" - #include <regex> namespace Star { @@ -40,34 +38,42 @@ TextPositioning TextPositioning::translated(Vec2F translation) const { TextPainter::TextPainter(RendererPtr renderer, TextureGroupPtr textureGroup) : m_renderer(renderer), m_fontTextureGroup(textureGroup), - m_fontSize(8), - m_lineSpacing(1.30f), - m_renderSettings({FontMode::Normal, Vec4B::filled(255), "hobo", ""}) { + m_defaultRenderSettings(), + m_renderSettings(), + m_savedRenderSettings() { reloadFonts(); m_reloadTracker = make_shared<TrackerListener>(); Root::singleton().registerReloadListener(m_reloadTracker); } RectF TextPainter::renderText(StringView s, TextPositioning const& position) { + RectF rect; if (position.charLimit) { unsigned charLimit = *position.charLimit; - return doRenderText(s, position, true, &charLimit); + rect = doRenderText(s, position, true, &charLimit); } else { - return doRenderText(s, position, true, nullptr); + rect = doRenderText(s, position, true, nullptr); } + renderPrimitives(); + return rect; } RectF TextPainter::renderLine(StringView s, TextPositioning const& position) { + RectF rect; if (position.charLimit) { unsigned charLimit = *position.charLimit; - return doRenderLine(s, position, true, &charLimit); + rect = doRenderLine(s, position, true, &charLimit); } else { - return doRenderLine(s, position, true, nullptr); + rect = doRenderLine(s, position, true, nullptr); } + renderPrimitives(); + return rect; } RectF TextPainter::renderGlyph(String::Char c, TextPositioning const& position) { - return doRenderGlyph(c, position, true); + auto rect = doRenderGlyph(c, position, true); + renderPrimitives(); + return rect; } RectF TextPainter::determineTextSize(StringView s, TextPositioning const& position) { @@ -83,34 +89,36 @@ RectF TextPainter::determineGlyphSize(String::Char c, TextPositioning const& pos } int TextPainter::glyphWidth(String::Char c) { - return m_fontTextureGroup.glyphWidth(c, m_fontSize); + return m_fontTextureGroup.glyphWidth(c, m_renderSettings.fontSize); } -int TextPainter::stringWidth(StringView s) { +int TextPainter::stringWidth(StringView s, unsigned charLimit) { if (s.empty()) return 0; String font = m_renderSettings.font, setFont = font; m_fontTextureGroup.switchFont(font); - int width = 0; - Text::CommandsCallback commandsCallback = [&](StringView commands) { commands.forEachSplitView(",", [&](StringView command, size_t, size_t) { - if (command == "reset") + if (command == "reset") { m_fontTextureGroup.switchFont(font = setFont); - else if (command == "set") + } else if (command == "set") { setFont = font; - else if (command.beginsWith("font=")) + } else if (command.beginsWith("font=")) { m_fontTextureGroup.switchFont(font = command.substr(5)); + } }); return true; }; + int width = 0; Text::TextCallback textCallback = [&](StringView text) { - for (String::Char c : text) + for (String::Char c : text) { width += glyphWidth(c); - + if (charLimit && --charLimit == 0) + return false; + } return true; }; @@ -122,7 +130,7 @@ int TextPainter::stringWidth(StringView s) { bool TextPainter::processWrapText(StringView text, unsigned* wrapWidth, WrapTextCallback textFunc) { String font = m_renderSettings.font, setFont = font; m_fontTextureGroup.switchFont(font); - int lines = 0; + unsigned lines = 0; auto it = text.begin(); auto end = text.end(); @@ -149,14 +157,15 @@ bool TextPainter::processWrapText(StringView text, unsigned* wrapWidth, WrapText if (escIt != end) { if (character == Text::EndEsc) { - StringView inner = slice(escIt, it); + StringView inner = slice(++escIt, it); inner.forEachSplitView(",", [&](StringView command, size_t, size_t) { - if (command == "reset") + if (command == "reset") { m_fontTextureGroup.switchFont(font = setFont); - else if (command == "set") + } else if (command == "set") { setFont = font; - else if (command.beginsWith("font=")) + } else if (command.beginsWith("font=")) { m_fontTextureGroup.switchFont(font = command.substr(5)); + } }); escIt = end; } @@ -177,7 +186,6 @@ bool TextPainter::processWrapText(StringView text, unsigned* wrapWidth, WrapText splitIt = end; } else { int charWidth = glyphWidth(character); - // is it a place where we might want to split the line ? if (character == ' ' || character == '\t') { splitIt = it; @@ -220,36 +228,20 @@ bool TextPainter::processWrapText(StringView text, unsigned* wrapWidth, WrapText List<StringView> TextPainter::wrapTextViews(StringView s, Maybe<unsigned> wrapWidth) { List<StringView> views = {}; - - bool active = false; - StringView current; - int lastLine = 0; - - auto addText = [&active, ¤t](StringView text) { - // Merge views if they are adjacent - if (active && current.utf8Ptr() + current.utf8Size() == text.utf8Ptr()) - current = StringView(current.utf8Ptr(), current.utf8Size() + text.utf8Size()); - else - current = text; - active = true; - }; - - TextPainter::WrapTextCallback textCallback = [&](StringView text, int line) { - if (lastLine != line) { - views.push_back(current); - lastLine = line; - active = false; + auto last = views.end(); + unsigned curLine = 0; + TextPainter::WrapTextCallback textCallback = [&](StringView text, unsigned line) { + if (line == curLine && last != views.end() && last->end() == text.begin()) { + *last = StringView(last->utf8Ptr(), last->utf8Size() + text.utf8Size()); + } else { + last = views.insert(views.end(), text); + curLine = line; } - - addText(text); return true; }; processWrapText(s, wrapWidth.ptr(), textCallback); - if (active) - views.push_back(current); - return views; } @@ -258,12 +250,11 @@ StringList TextPainter::wrapText(StringView s, Maybe<unsigned> wrapWidth) { String current; int lastLine = 0; - TextPainter::WrapTextCallback textCallback = [&](StringView text, int line) { + TextPainter::WrapTextCallback textCallback = [&](StringView text, unsigned line) { if (lastLine != line) { result.append(std::move(current)); lastLine = line; } - current += text; return true; }; @@ -277,40 +268,45 @@ StringList TextPainter::wrapText(StringView s, Maybe<unsigned> wrapWidth) { }; unsigned TextPainter::fontSize() const { - return m_fontSize; + return m_renderSettings.fontSize; } void TextPainter::setFontSize(unsigned size) { - m_fontSize = size; + m_renderSettings.fontSize = size; } void TextPainter::setLineSpacing(float lineSpacing) { - m_lineSpacing = lineSpacing; + m_renderSettings.lineSpacing = lineSpacing; } void TextPainter::setMode(FontMode mode) { - m_renderSettings.mode = mode; + m_renderSettings.shadow = fontModeToColor(mode).toRgba(); } void TextPainter::setFontColor(Vec4B color) { m_renderSettings.color = std::move(color); } -void TextPainter::setProcessingDirectives(StringView directives) { - m_renderSettings.directives = String(directives); - if (m_renderSettings.directives) { - m_renderSettings.directives.loadOperations(); - for (auto& entry : m_renderSettings.directives.shared->entries) { - if (auto border = entry.operation.ptr<BorderImageOperation>()) - border->includeTransparent = true; - } - } +void TextPainter::setProcessingDirectives(StringView directives, bool back) { + Directives& target = back ? m_renderSettings.backDirectives : m_renderSettings.directives; + modifyDirectives(target = String(directives)); } void TextPainter::setFont(String const& font) { m_renderSettings.font = font; } +TextStyle& TextPainter::setTextStyle(TextStyle const& textStyle) { + TextStyle& style = m_renderSettings = textStyle; + modifyDirectives(style.directives); + modifyDirectives(style.backDirectives); + return style; +} + +void TextPainter::clearTextStyle() { + m_renderSettings = m_defaultRenderSettings; +} + void TextPainter::addFont(FontPtr const& font, String const& name) { m_fontTextureGroup.addFont(font, name); } @@ -348,16 +344,19 @@ void TextPainter::applyCommands(StringView unsplitCommands) { m_renderSettings = m_savedRenderSettings; } else if (command == "set") { m_savedRenderSettings = m_renderSettings; - } else if (command == "shadow") { - m_renderSettings.mode = (FontMode)((int)m_renderSettings.mode | (int)FontMode::Shadow); + } else if (command.beginsWith("shadow")) { + if (command.utf8Size() == 6) + m_renderSettings.shadow = Color::Black.toRgba(); + else if (command[6] == '=') + m_renderSettings.shadow = Color(command.substr(7)).toRgba(); } else if (command == "noshadow") { - m_renderSettings.mode = (FontMode)((int)m_renderSettings.mode & (-1 ^ (int)FontMode::Shadow)); + m_renderSettings.shadow = Color::Clear.toRgba(); } else if (command.beginsWith("font=")) { m_renderSettings.font = command.substr(5); } else if (command.beginsWith("directives=")) { - // Honestly this is really stupid but I just couldn't help myself - // Should probably limit in the future setProcessingDirectives(command.substr(11)); + } else if (command.beginsWith("backdirectives=")) { + setProcessingDirectives(command.substr(15), true); } else { // expects both #... sequences and plain old color names. Color c = Color(command); @@ -370,6 +369,16 @@ void TextPainter::applyCommands(StringView unsplitCommands) { }); } +void TextPainter::modifyDirectives(Directives& directives) { + if (directives) { + directives.loadOperations(); + for (auto& entry : directives->entries) { + if (auto border = entry.operation.ptr<BorderImageOperation>()) + border->includeTransparent = true; + } + } +} + RectF TextPainter::doRenderText(StringView s, TextPositioning const& position, bool reallyRender, unsigned* charLimit) { Vec2F pos = position.pos; if (s.empty()) @@ -377,26 +386,23 @@ RectF TextPainter::doRenderText(StringView s, TextPositioning const& position, b List<StringView> lines = wrapTextViews(s, position.wrapWidth); - int height = (lines.size() - 1) * m_lineSpacing * m_fontSize + m_fontSize; - - RenderSettings backupRenderSettings = m_renderSettings; - m_savedRenderSettings = m_renderSettings; - + TextStyle backup = m_savedRenderSettings = m_renderSettings; + int height = (lines.size() - 1) * backup.lineSpacing * backup.fontSize + backup.fontSize; if (position.vAnchor == VerticalAnchor::BottomAnchor) - pos[1] += (height - m_fontSize); + pos[1] += (height - backup.fontSize); else if (position.vAnchor == VerticalAnchor::VMidAnchor) - pos[1] += (height - m_fontSize) / 2; + pos[1] += (height - backup.fontSize) / 2; RectF bounds = RectF::withSize(pos, Vec2F()); for (auto& i : lines) { bounds.combine(doRenderLine(i, { pos, position.hAnchor, position.vAnchor }, reallyRender, charLimit)); - pos[1] -= m_fontSize * m_lineSpacing; + pos[1] -= m_renderSettings.fontSize * m_renderSettings.lineSpacing; if (charLimit && *charLimit == 0) break; } - m_renderSettings = std::move(backupRenderSettings); + m_renderSettings = std::move(backup); return bounds; } @@ -425,9 +431,8 @@ RectF TextPainter::doRenderLine(StringView text, TextPositioning const& position if (*charLimit == 0) return false; else - --* charLimit; + --*charLimit; } - RectF glyphBounds = doRenderGlyph(c, pos, reallyRender); bounds.combine(glyphBounds); pos.pos[0] += glyphBounds.width(); @@ -461,41 +466,58 @@ RectF TextPainter::doRenderGlyph(String::Char c, TextPositioning const& position float vOffset = 0; if (position.vAnchor == VerticalAnchor::VMidAnchor) - vOffset = -floor((float)m_fontSize / 2); + vOffset = -floor((float)m_renderSettings.fontSize / 2); else if (position.vAnchor == VerticalAnchor::TopAnchor) - vOffset = -(float)m_fontSize; + vOffset = -(float)m_renderSettings.fontSize; Directives* directives = m_renderSettings.directives ? &m_renderSettings.directives : nullptr; + Vec2F pos = position.pos + Vec2F(hOffset, vOffset); if (reallyRender) { - if ((int)m_renderSettings.mode & (int)FontMode::Shadow) { - Color shadow = Color::Black; - uint8_t alphaU = m_renderSettings.color[3]; + bool hasShadow = m_renderSettings.shadow[3] > 0; + bool hasBackDirectives = m_renderSettings.backDirectives; + if (hasShadow) { + //Kae: unlike vanilla we draw only one shadow glyph instead of two, so i'm tweaking the alpha here + Vec4B shadow = m_renderSettings.shadow; + uint8_t alphaU = m_renderSettings.color[3] * byteToFloat(shadow[3]); if (alphaU != 255) { float alpha = byteToFloat(alphaU); - shadow.setAlpha(floatToByte(alpha * (1.5f - 0.5f * alpha))); + shadow[3] = floatToByte(alpha * (1.5f - 0.5f * alpha)); } else - shadow.setAlpha(alphaU); + shadow[3] = alphaU; - //Kae: Draw only one shadow glyph instead of stacking two, alpha modified to appear perceptually the same as vanilla - renderGlyph(c, position.pos + Vec2F(hOffset, vOffset - 2), m_fontSize, 1, shadow.toRgba(), directives); + Directives const* shadowDirectives = hasBackDirectives ? &m_renderSettings.backDirectives : directives; + renderGlyph(c, pos + Vec2F(0, -2), m_shadowPrimitives, m_renderSettings.fontSize, 1, shadow, shadowDirectives); } + if (hasBackDirectives) + renderGlyph(c, pos, m_backPrimitives, m_renderSettings.fontSize, 1, m_renderSettings.color, &m_renderSettings.backDirectives); - renderGlyph(c, position.pos + Vec2F(hOffset, vOffset), m_fontSize, 1, m_renderSettings.color, directives); + auto& output = (hasShadow || hasBackDirectives) ? m_frontPrimitives : m_renderer->immediatePrimitives(); + renderGlyph(c, pos, output, m_renderSettings.fontSize, 1, m_renderSettings.color, directives); } - return RectF::withSize(position.pos + Vec2F(hOffset, vOffset), {(float)width, (int)m_fontSize}); + return RectF::withSize(pos, {(float)width, (int)m_renderSettings.fontSize}); +} + +void TextPainter::renderPrimitives() { + auto& destination = m_renderer->immediatePrimitives(); + std::move(std::begin(m_shadowPrimitives), std::end(m_shadowPrimitives), std::back_inserter(destination)); + m_shadowPrimitives.clear(); + std::move(std::begin(m_backPrimitives), std::end(m_backPrimitives), std::back_inserter(destination)); + m_backPrimitives.clear(); + std::move(std::begin(m_frontPrimitives), std::end(m_frontPrimitives), std::back_inserter(destination)); + m_frontPrimitives.clear(); } -void TextPainter::renderGlyph(String::Char c, Vec2F const& screenPos, unsigned fontSize, +void TextPainter::renderGlyph(String::Char c, Vec2F const& screenPos, List<RenderPrimitive>& out, unsigned fontSize, float scale, Vec4B const& color, Directives const* processingDirectives) { if (!fontSize) return; const FontTextureGroup::GlyphTexture& glyphTexture = m_fontTextureGroup.glyphTexture(c, fontSize, processingDirectives); Vec2F offset = glyphTexture.offset * scale; - m_renderer->immediatePrimitives().emplace_back(std::in_place_type_t<RenderQuad>(), glyphTexture.texture, Vec2F::round(screenPos + offset), scale, color, 0.0f); + out.emplace_back(std::in_place_type_t<RenderQuad>(), glyphTexture.texture, Vec2F::round(screenPos + offset), scale, color, 0.0f); } FontPtr TextPainter::loadFont(String const& fontPath, Maybe<String> fontName) { diff --git a/source/rendering/StarTextPainter.hpp b/source/rendering/StarTextPainter.hpp index a0db304..bff3a64 100644 --- a/source/rendering/StarTextPainter.hpp +++ b/source/rendering/StarTextPainter.hpp @@ -4,17 +4,21 @@ #include "StarAnchorTypes.hpp" #include "StarRoot.hpp" #include "StarStringView.hpp" +#include "StarText.hpp" namespace Star { -STAR_CLASS(TextPainter); - -enum class FontMode { +// deprecated in favor of explicit shadow color +enum class FontMode : uint8_t { Normal, Shadow }; -float const DefaultLineSpacing = 1.3f; +inline Color const& fontModeToColor(FontMode mode) { + return mode == FontMode::Shadow ? Color::Black : Color::Clear; +} + +STAR_CLASS(TextPainter); struct TextPositioning { TextPositioning(); @@ -52,10 +56,10 @@ public: RectF determineGlyphSize(String::Char c, TextPositioning const& position); int glyphWidth(String::Char c); - int stringWidth(StringView s); + int stringWidth(StringView s, unsigned charLimit = 0); - typedef function<bool(StringView, int)> WrapTextCallback; + typedef function<bool(StringView, unsigned)> WrapTextCallback; bool processWrapText(StringView s, unsigned* wrapWidth, WrapTextCallback textFunc); List<StringView> wrapTextViews(StringView s, Maybe<unsigned> wrapWidth); @@ -66,39 +70,36 @@ public: void setLineSpacing(float lineSpacing); void setMode(FontMode mode); void setFontColor(Vec4B color); - void setProcessingDirectives(StringView directives); + void setProcessingDirectives(StringView directives, bool back = false); void setFont(String const& font); + TextStyle& setTextStyle(TextStyle const& textStyle); + void clearTextStyle(); void addFont(FontPtr const& font, String const& name); void reloadFonts(); void cleanup(int64_t textureTimeout); void applyCommands(StringView unsplitCommands); private: - struct RenderSettings { - FontMode mode; - Vec4B color; - String font; - Directives directives; - }; - + void modifyDirectives(Directives& directives); RectF doRenderText(StringView s, TextPositioning const& position, bool reallyRender, unsigned* charLimit); RectF doRenderLine(StringView s, TextPositioning const& position, bool reallyRender, unsigned* charLimit); RectF doRenderGlyph(String::Char c, TextPositioning const& position, bool reallyRender); - void renderGlyph(String::Char c, Vec2F const& screenPos, unsigned fontSize, float scale, Vec4B const& color, Directives const* processingDirectives = nullptr); + void renderPrimitives(); + void renderGlyph(String::Char c, Vec2F const& screenPos, List<RenderPrimitive>& out, unsigned fontSize, float scale, Vec4B const& color, Directives const* processingDirectives = nullptr); static FontPtr loadFont(String const& fontPath, Maybe<String> fontName = {}); RendererPtr m_renderer; + List<RenderPrimitive> m_shadowPrimitives; + List<RenderPrimitive> m_backPrimitives; + List<RenderPrimitive> m_frontPrimitives; FontTextureGroup m_fontTextureGroup; - unsigned m_fontSize; - float m_lineSpacing; - - RenderSettings m_renderSettings; - RenderSettings m_savedRenderSettings; + TextStyle m_defaultRenderSettings; + TextStyle m_renderSettings; + TextStyle m_savedRenderSettings; String m_nonRenderedCharacters; - TrackerListenerPtr m_reloadTracker; }; |