From 7fbcfeb35c1b930ddcdd151125b2c33aa8f6786d Mon Sep 17 00:00:00 2001 From: thebigMuh Date: Sat, 15 May 2021 03:36:19 +0200 Subject: [PATCH] Adding text rendering flag to adjust spacing to fit horizontally Now also works for multiline strings --- Source/DiabloUI/ui_item.h | 29 +++++++-------- Source/control.cpp | 4 +-- Source/engine/render/text_render.cpp | 53 ++++++++++++++++++++++------ Source/engine/render/text_render.hpp | 11 +++++- 4 files changed, 69 insertions(+), 28 deletions(-) diff --git a/Source/DiabloUI/ui_item.h b/Source/DiabloUI/ui_item.h index 86cba889a..c5beaf1b9 100644 --- a/Source/DiabloUI/ui_item.h +++ b/Source/DiabloUI/ui_item.h @@ -24,20 +24,21 @@ enum UiType : uint8_t { enum UiFlags : uint16_t { // clang-format off - UIS_SMALL = 1 << 0, - UIS_MED = 1 << 1, - UIS_BIG = 1 << 2, - UIS_HUGE = 1 << 3, - UIS_CENTER = 1 << 4, - UIS_RIGHT = 1 << 5, - UIS_VCENTER = 1 << 6, - UIS_SILVER = 1 << 7, - UIS_GOLD = 1 << 8, - UIS_RED = 1 << 9, - UIS_BLUE = 1 << 10, - UIS_BLACK = 1 << 11, - UIS_DISABLED = 1 << 12, - UIS_HIDDEN = 1 << 13, + UIS_SMALL = 1 << 0, + UIS_MED = 1 << 1, + UIS_BIG = 1 << 2, + UIS_HUGE = 1 << 3, + UIS_CENTER = 1 << 4, + UIS_RIGHT = 1 << 5, + UIS_VCENTER = 1 << 6, + UIS_SILVER = 1 << 7, + UIS_GOLD = 1 << 8, + UIS_RED = 1 << 9, + UIS_BLUE = 1 << 10, + UIS_BLACK = 1 << 11, + UIS_DISABLED = 1 << 12, + UIS_HIDDEN = 1 << 13, + UIS_FIT_SPACING = 1 << 14, // clang-format on }; diff --git a/Source/control.cpp b/Source/control.cpp index 9fc00eeba..48a2b4c10 100644 --- a/Source/control.cpp +++ b/Source/control.cpp @@ -1095,14 +1095,14 @@ static void PrintInfo(const CelOutputBuffer &out) int yo = 0; int lo = 1; if (infostr[0] != '\0') { - DrawString(out, infostr, line, infoclr | UIS_CENTER, 2); + DrawString(out, infostr, line, infoclr | UIS_CENTER | UIS_FIT_SPACING, 2); yo = 1; lo = 0; } for (int i = 0; i < pnumlines; i++) { line.y = PANEL_Y + LineOffsets[pnumlines - lo][i + yo]; - DrawString(out, panelstr[i], line, infoclr | UIS_CENTER, 2); + DrawString(out, panelstr[i], line, infoclr | UIS_CENTER | UIS_FIT_SPACING, 2); } } diff --git a/Source/engine/render/text_render.cpp b/Source/engine/render/text_render.cpp index 6a6d583b2..e9839e39c 100644 --- a/Source/engine/render/text_render.cpp +++ b/Source/engine/render/text_render.cpp @@ -194,12 +194,13 @@ void PrintChar(const CelOutputBuffer &out, int sx, int sy, int nCel, text_color } } -int GetLineWidth(const char *text, GameFontTables size, int spacing) +int GetLineWidth(const char *text, GameFontTables size, int spacing, int *charactersInLine) { int lineWidth = 0; size_t textLength = strlen(text); - for (unsigned i = 0; i < textLength; i++) { + size_t i = 0; + for (; i < textLength; i++) { if (text[i] == '\n') break; @@ -207,9 +208,23 @@ int GetLineWidth(const char *text, GameFontTables size, int spacing) lineWidth += fontkern[size][frame] + spacing; } + if (charactersInLine != nullptr) + *charactersInLine = i; + return lineWidth != 0 ? (lineWidth - spacing) : 0; } +int AdjustSpacingToFitHorizontally(int &lineWidth, int maxSpacing, int charactersInLine, int availableWidth) +{ + if (lineWidth <= availableWidth || charactersInLine < 2) + return maxSpacing; + + const int overhang = lineWidth - availableWidth; + const int spacingRedux = (overhang + charactersInLine - 2) / (charactersInLine - 1); + lineWidth -= spacingRedux * (charactersInLine - 1); + return maxSpacing - spacingRedux; +} + void WordWrapGameString(char *text, size_t width, size_t size, int spacing) { const size_t textLength = strlen(text); @@ -272,28 +287,44 @@ void DrawString(const CelOutputBuffer &out, const char *text, const SDL_Rect &re color = COL_BLACK; const int w = rect.w != 0 ? rect.w : out.w() - rect.x; - const int h = rect.h != 0 ? rect.h : out.h() - rect.x; + const int h = rect.h != 0 ? rect.h : out.h() - rect.y; + + const size_t textLength = strlen(text); + + int charactersInLine = 0; + int lineWidth = 0; + if ((flags & (UIS_CENTER | UIS_RIGHT | UIS_FIT_SPACING)) != 0) + lineWidth = GetLineWidth(text, size, spacing, &charactersInLine); + + int maxSpacing = spacing; + if ((flags & UIS_FIT_SPACING) != 0) + spacing = AdjustSpacingToFitHorizontally(lineWidth, maxSpacing, charactersInLine, rect.w); int sx = rect.x; if ((flags & UIS_CENTER) != 0) - sx += (w - GetLineWidth(text, size, spacing)) / 2; + sx += (w - lineWidth) / 2; else if ((flags & UIS_RIGHT) != 0) - sx += w - GetLineWidth(text, size, spacing); + sx += w - lineWidth; int sy = rect.y; int rightMargin = rect.x + w; int bottomMargin = rect.y + h; - const size_t textLength = strlen(text); for (unsigned i = 0; i < textLength; i++) { uint8_t frame = fontframe[size][gbFontTransTbl[static_cast(text[i])]]; - int symbolWidth = fontkern[size][frame] + spacing; - if (text[i] == '\n' || sx + symbolWidth - spacing > rightMargin) { + int symbolWidth = fontkern[size][frame]; + if (text[i] == '\n' || sx + symbolWidth > rightMargin) { + if ((flags & (UIS_CENTER | UIS_RIGHT | UIS_FIT_SPACING)) != 0) + lineWidth = GetLineWidth(&text[i + 1], size, spacing, &charactersInLine); + + if ((flags & UIS_FIT_SPACING) != 0) + spacing = AdjustSpacingToFitHorizontally(lineWidth, maxSpacing, charactersInLine, rect.w); + sx = rect.x; if ((flags & UIS_CENTER) != 0) - sx += (w - GetLineWidth(&text[i + 1], size)) / 2; + sx += (w - lineWidth) / 2; else if ((flags & UIS_RIGHT) != 0) - sx += w - GetLineWidth(&text[i + 1], size, spacing); + sx += w - lineWidth; sy += LineHeights[size]; if (sy > bottomMargin) return; @@ -302,7 +333,7 @@ void DrawString(const CelOutputBuffer &out, const char *text, const SDL_Rect &re PrintChar(out, sx, sy, frame, color); } if (text[i] != '\n') - sx += symbolWidth; + sx += symbolWidth + spacing; } if (drawTextCursor) { CelDrawTo(out, sx, sy, *pSPentSpn2Cels, PentSpn2Spin()); diff --git a/Source/engine/render/text_render.hpp b/Source/engine/render/text_render.hpp index fb133f5e7..f782238aa 100644 --- a/Source/engine/render/text_render.hpp +++ b/Source/engine/render/text_render.hpp @@ -50,7 +50,16 @@ void FreeText(); * @param col text_color color value */ void PrintChar(const CelOutputBuffer &out, int sx, int sy, int nCel, text_color col); -int GetLineWidth(const char *text, GameFontTables size = GameFontSmall, int spacing = 1); + +/** + * @brief Calculate pixel width of first line of text, respecting kerning + * @param text Text to check, will read until first eol or terminator + * @param size Font size to use + * @param spacing Extra spacing to add per character + * @param charactersInLine Receives characters read until newline or terminator + * @return Line width in pixels + */ +int GetLineWidth(const char *text, GameFontTables size = GameFontSmall, int spacing = 1, int *charactersInLine = nullptr); void WordWrapGameString(char *text, size_t width, size_t size = GameFontSmall, int spacing = 1); void DrawString(const CelOutputBuffer &out, const char *text, const SDL_Rect &rect, uint16_t flags = 0, int spacing = 1, bool drawTextCursor = false); int PentSpn2Spin();