Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix item description rendering issues #7683

Merged
merged 3 commits into from
Jan 29, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
68 changes: 39 additions & 29 deletions Source/engine/render/text_render.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
*/
#include "text_render.hpp"

#include <algorithm>
#include <array>
#include <cstddef>
#include <cstdint>
Expand Down Expand Up @@ -381,6 +382,17 @@ Surface ClipSurface(const Surface &out, Rectangle rect)
std::min(rect.position.y + rect.size.height, out.h()));
}

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 MaybeWrap(Point &characterPosition, int characterWidth, int rightMargin, int initialX, int lineHeight)
{
if (characterPosition.x + characterWidth > rightMargin) {
Expand All @@ -391,18 +403,22 @@ void MaybeWrap(Point &characterPosition, int characterWidth, int rightMargin, in

int GetLineStartX(UiFlags flags, const Rectangle &rect, int lineWidth)
{
if (HasAnyOf(flags, UiFlags::AlignCenter))
return rect.position.x + (rect.size.width - lineWidth) / 2;
if (HasAnyOf(flags, UiFlags::AlignCenter)) {
return std::max(rect.position.x, rect.position.x + (rect.size.width - lineWidth) / 2);
}
if (HasAnyOf(flags, UiFlags::AlignRight))
return rect.position.x + rect.size.width - lineWidth;
return rect.position.x;
}

uint32_t DoDrawString(const Surface &out, std::string_view text, Rectangle rect, Point &characterPosition,
int lineWidth, int rightMargin, int bottomMargin, GameFontTables size, text_color color, bool outline,
int lineWidth, int charactersInLine, int rightMargin, int bottomMargin, GameFontTables size, text_color color, bool outline,
TextRenderOptions &opts)
{
CurrentFont currentFont;
int curSpacing = HasAnyOf(opts.flags, UiFlags::KerningFitSpacing)
? AdjustSpacingToFitHorizontally(lineWidth, opts.spacing, charactersInLine, rect.size.width)
: opts.spacing;

char32_t next;
std::string_view remaining = text;
Expand Down Expand Up @@ -446,10 +462,15 @@ uint32_t DoDrawString(const Surface &out, std::string_view text, Rectangle rect,
break;
characterPosition.y = nextLineY;

if (HasAnyOf(opts.flags, UiFlags::KerningFitSpacing)) {
int nextLineWidth = GetLineWidth(remaining.substr(cpLen), size, opts.spacing, &charactersInLine);
curSpacing = AdjustSpacingToFitHorizontally(nextLineWidth, opts.spacing, charactersInLine, rect.size.width);
}

if (HasAnyOf(opts.flags, (UiFlags::AlignCenter | UiFlags::AlignRight))) {
lineWidth = width;
if (remaining.size() > cpLen)
lineWidth += opts.spacing + GetLineWidth(remaining.substr(cpLen), size, opts.spacing);
lineWidth += curSpacing + GetLineWidth(remaining.substr(cpLen), size, curSpacing);
}
characterPosition.x = GetLineStartX(opts.flags, rect, lineWidth);

Expand All @@ -464,13 +485,13 @@ uint32_t DoDrawString(const Surface &out, std::string_view text, Rectangle rect,
if (byteIndex >= opts.highlightRange.begin && byteIndex < opts.highlightRange.end) {
const bool lastInRange = static_cast<int>(byteIndex + cpLen) == opts.highlightRange.end;
FillRect(out, characterPosition.x, characterPosition.y,
glyph.width() + (lastInRange ? 0 : opts.spacing), glyph.height(),
glyph.width() + (lastInRange ? 0 : curSpacing), glyph.height(),
opts.highlightColor);
}

DrawFont(out, characterPosition, glyph, color, outline);
maybeDrawCursor();
characterPosition.x += width + opts.spacing;
characterPosition.x += width + curSpacing;
}
maybeDrawCursor();
return static_cast<uint32_t>(remaining.data() - text.data());
Expand Down Expand Up @@ -591,17 +612,6 @@ int GetLineHeight(std::string_view text, GameFontTables fontIndex)
return LineHeights[fontIndex];
}

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;
}

std::string WordWrapString(std::string_view text, unsigned width, GameFontTables size, int spacing)
{
std::string output;
Expand Down Expand Up @@ -698,10 +708,6 @@ uint32_t DrawString(const Surface &out, std::string_view text, const Rectangle &
if (HasAnyOf(opts.flags, (UiFlags::AlignCenter | UiFlags::AlignRight | UiFlags::KerningFitSpacing)))
lineWidth = GetLineWidth(text, size, opts.spacing, &charactersInLine);

const int maxSpacing = opts.spacing;
if (HasAnyOf(opts.flags, UiFlags::KerningFitSpacing))
opts.spacing = AdjustSpacingToFitHorizontally(lineWidth, maxSpacing, charactersInLine, rect.size.width);

Point characterPosition { GetLineStartX(opts.flags, rect, lineWidth), rect.position.y };
const int initialX = characterPosition.x;

Expand All @@ -728,7 +734,7 @@ uint32_t DrawString(const Surface &out, std::string_view text, const Rectangle &
}

const uint32_t bytesDrawn = DoDrawString(clippedOut, text, rect, characterPosition,
lineWidth, rightMargin, bottomMargin, size, color, outlined, opts);
lineWidth, charactersInLine, rightMargin, bottomMargin, size, color, outlined, opts);

if (HasAnyOf(opts.flags, UiFlags::PentaCursor)) {
const ClxSprite sprite = (*pSPentSpn2Cels)[PentSpn2Spin()];
Expand All @@ -749,10 +755,6 @@ void DrawStringWithColors(const Surface &out, std::string_view fmt, DrawStringFo
if (HasAnyOf(opts.flags, (UiFlags::AlignCenter | UiFlags::AlignRight | UiFlags::KerningFitSpacing)))
lineWidth = GetLineWidth(fmt, args, argsLen, 0, size, opts.spacing, &charactersInLine);

const int maxSpacing = opts.spacing;
if (HasAnyOf(opts.flags, UiFlags::KerningFitSpacing))
opts.spacing = AdjustSpacingToFitHorizontally(lineWidth, maxSpacing, charactersInLine, rect.size.width);

Point characterPosition { GetLineStartX(opts.flags, rect, lineWidth), rect.position.y };
const int initialX = characterPosition.x;

Expand All @@ -774,6 +776,9 @@ void DrawStringWithColors(const Surface &out, std::string_view fmt, DrawStringFo
const Surface clippedOut = ClipSurface(out, rect);

CurrentFont currentFont;
int curSpacing = HasAnyOf(opts.flags, UiFlags::KerningFitSpacing)
? AdjustSpacingToFitHorizontally(lineWidth, opts.spacing, charactersInLine, rect.size.width)
: opts.spacing;

char32_t prev = U'\0';
char32_t next;
Expand All @@ -789,7 +794,7 @@ void DrawStringWithColors(const Surface &out, std::string_view fmt, DrawStringFo

const std::optional<std::size_t> fmtArgPos = fmtArgParser(remaining);
if (fmtArgPos) {
DoDrawString(clippedOut, args[*fmtArgPos].GetFormatted(), rect, characterPosition, lineWidth, rightMargin, bottomMargin, size,
DoDrawString(clippedOut, args[*fmtArgPos].GetFormatted(), rect, characterPosition, lineWidth, charactersInLine, rightMargin, bottomMargin, size,
GetColorFromFlags(args[*fmtArgPos].GetFlags()), outlined, opts);
// `fmtArgParser` has already consumed `remaining`. Ensure the loop doesn't consume any more.
cpLen = 0;
Expand All @@ -814,10 +819,15 @@ void DrawStringWithColors(const Surface &out, std::string_view fmt, DrawStringFo
break;
characterPosition.y = nextLineY;

if (HasAnyOf(opts.flags, UiFlags::KerningFitSpacing)) {
int nextLineWidth = GetLineWidth(remaining.substr(cpLen), args, argsLen, fmtArgParser.offset(), size, opts.spacing, &charactersInLine);
curSpacing = AdjustSpacingToFitHorizontally(nextLineWidth, opts.spacing, charactersInLine, rect.size.width);
}

if (HasAnyOf(opts.flags, (UiFlags::AlignCenter | UiFlags::AlignRight))) {
lineWidth = width;
if (remaining.size() > cpLen)
lineWidth += opts.spacing + GetLineWidth(remaining.substr(cpLen), args, argsLen, fmtArgParser.offset(), size, opts.spacing);
lineWidth += curSpacing + GetLineWidth(remaining.substr(cpLen), args, argsLen, fmtArgParser.offset(), size, curSpacing);
}
characterPosition.x = GetLineStartX(opts.flags, rect, lineWidth);

Expand All @@ -826,7 +836,7 @@ void DrawStringWithColors(const Surface &out, std::string_view fmt, DrawStringFo
}

DrawFont(clippedOut, characterPosition, (*currentFont.sprite)[frame], color, outlined);
characterPosition.x += width + opts.spacing;
characterPosition.x += width + curSpacing;
}

if (HasAnyOf(opts.flags, UiFlags::PentaCursor)) {
Expand Down
13 changes: 11 additions & 2 deletions Source/items.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -44,13 +44,15 @@
#include "qol/stash.h"
#include "spells.h"
#include "stores.h"
#include "utils/algorithm/container.hpp"
#include "utils/format_int.hpp"
#include "utils/is_of.hpp"
#include "utils/language.h"
#include "utils/log.hpp"
#include "utils/math.h"
#include "utils/str_case.hpp"
#include "utils/str_cat.hpp"
#include "utils/str_split.hpp"
#include "utils/utf8.hpp"

namespace devilution {
Expand Down Expand Up @@ -4098,12 +4100,19 @@ void DrawUniqueInfo(const Surface &out)

rect.position.y += (10 - uitem.UINumPL) * 12;
assert(uitem.UINumPL <= sizeof(uitem.powers) / sizeof(*uitem.powers));
const TextRenderOptions textRenderOptions { .flags = UiFlags::ColorWhite | UiFlags::AlignCenter };
const GameFontTables fontSize = GetFontSizeFromUiFlags(textRenderOptions.flags);
for (const auto &power : uitem.powers) {
if (power.type == IPL_INVALID)
break;
rect.position.y += 2 * 12;
DrawString(out, PrintItemPower(power.type, curruitem), rect,
{ .flags = UiFlags::ColorWhite | UiFlags::AlignCenter });
// Pre-wrap the string at spaces, otherwise DrawString would hard wrap in the middle of words.
const std::string wrapped = WordWrapString(PrintItemPower(power.type, curruitem), rect.size.width);
DrawString(out, wrapped, rect, textRenderOptions);
for (const std::string_view line : SplitByChar(wrapped, '\n')) {
if (line.data() + line.size() == wrapped.data() + wrapped.size()) break;
rect.position.y += GetLineHeight(line, fontSize);
}
}
}

Expand Down
Loading