[Scummvm-git-logs] scummvm master -> a55c08c7ec005f2b17a8b0d7d6a2fbd7ea81ee03
aquadran
noreply at scummvm.org
Sat Aug 1 16:03:19 UTC 2026
This automated email contains information about 1 new commit which have been
pushed to the 'scummvm' repo located at https://api.github.com/repos/scummvm/scummvm .
Summary:
a55c08c7ec WINTERMUTE: Sync TT font code with original WME lite
Commit: a55c08c7ec005f2b17a8b0d7d6a2fbd7ea81ee03
https://github.com/scummvm/scummvm/commit/a55c08c7ec005f2b17a8b0d7d6a2fbd7ea81ee03
Author: PaweÅ KoÅodziejski (aquadran at gmail.com)
Date: 2026-08-01T18:03:05+02:00
Commit Message:
WINTERMUTE: Sync TT font code with original WME lite
Changed paths:
engines/wintermute/base/font/base_font_truetype.cpp
engines/wintermute/base/font/base_font_truetype.h
diff --git a/engines/wintermute/base/font/base_font_truetype.cpp b/engines/wintermute/base/font/base_font_truetype.cpp
index 354d89a8dc7..ca890b1b6f5 100644
--- a/engines/wintermute/base/font/base_font_truetype.cpp
+++ b/engines/wintermute/base/font/base_font_truetype.cpp
@@ -110,7 +110,6 @@ int BaseFontTT::getTextWidth(const byte *text, int maxLength) {
if (maxLength >= 0 && (int)textStr.size() > maxLength) {
textStr = textStr.substr(0, (uint32)maxLength);
}
- //text = text.substr(0, MaxLength); // TODO: Remove
int textWidth, textHeight;
measureText(textStr, -1, -1, textWidth, textHeight);
@@ -144,9 +143,6 @@ void BaseFontTT::drawText(const byte *text, int x, int y, int width, TTextAlign
WideString textStr;
- // TODO: Why do we still insist on Widestrings everywhere?
- // HACK: J.U.L.I.A. uses CP1252, we need to fix that,
- // And we still don't have any UTF8-support.
if (_game->_textEncoding == TEXT_UTF8) {
textStr = StringUtil::utf8ToWide((const char *)text);
} else {
@@ -156,7 +152,6 @@ void BaseFontTT::drawText(const byte *text, int x, int y, int width, TTextAlign
if (maxLength >= 0 && textStr.size() > (uint32)maxLength) {
textStr = textStr.substr(0, (uint32)maxLength);
}
- //text = text.substr(0, MaxLength); // TODO: Remove
BaseRenderer *renderer = _game->_renderer;
@@ -232,19 +227,38 @@ void BaseFontTT::drawText(const byte *text, int x, int y, int width, TTextAlign
//////////////////////////////////////////////////////////////////////////
BaseSurface *BaseFontTT::renderTextToTexture(const WideString &text, int width, TTextAlign align, int maxHeight, int &textOffset) {
- //TextLineList lines;
- // TODO: Use WideString-conversion here.
- //WrapText(text, width, maxHeight, lines);
- Common::Array<WideString> lines;
- _font->wordWrapText(text, width, lines);
-
- while (maxHeight > 0 && lines.size() * _lineHeight > maxHeight) {
- lines.pop_back();
- }
- if (lines.size() == 0) {
- return nullptr;
+ TextLineList lines;
+ int32 heightAfterWrapping;
+
+ /* The text will be wrapped to fit into the width and height as specified.
+ * In case there was too much text, it is truncated as soon as maxHeight is exceeded.
+ * Here, the distance between two lines is taken from the _lineHeight value (coming from the font definition).
+ */
+ heightAfterWrapping = wrapText(text, width, maxHeight, lines);
+
+ TextLineList::iterator it;
+
+ /* The surface to render the text onto is equal in width, but the height is computed differently.
+ * _maxCharHeight is the Y size of the bounding box of all characters.
+ *
+ * There could be fonts that have incorrect parameters set. The freetype doc says that the value used
+ * for _lineHeight does not assure that all glyphs will "fit" into this.
+ *
+ * It won't be possible to "fix" everything, but at least try our best with obvious failures.
+ * So if the resulting text height from the computation of "WrapText" is bigger than the
+ * size computed below, adjust it appropriately. Later when drawing the glyphs, the _lineHeight
+ * is used anyway as line distance, so checking the "textHeight" for sanity is not a bad idea.
+ *
+ */
+ int32 textHeight = lines.size() * (_lineHeight + _font->getFontAscent());
+ if (heightAfterWrapping > textHeight) {
+ _game->LOG(0, "Strange font definitions. Text height %d smaller than line height %d.", textHeight, heightAfterWrapping);
+ textHeight = heightAfterWrapping;
}
+ Graphics::Surface *surface = new Graphics::Surface();
+ surface->create((uint16)width, (uint16)(_lineHeight * lines.size()), _game->_renderer->getPixelFormat());
+
Graphics::TextAlign alignment = Graphics::kTextAlignInvalid;
if (align == TAL_LEFT) {
alignment = Graphics::kTextAlignLeft;
@@ -254,20 +268,17 @@ BaseSurface *BaseFontTT::renderTextToTexture(const WideString &text, int width,
alignment = Graphics::kTextAlignRight;
}
- // TODO: This debug call does not work with WideString because text.c_str() returns an uint32 array.
- //debugC(kWintermuteDebugFont, "%s %d %d %d %d", text.c_str(), RGBCOLGetR(_layers[0]->_color), RGBCOLGetG(_layers[0]->_color), RGBCOLGetB(_layers[0]->_color), RGBCOLGetA(_layers[0]->_color));
-// void drawAlphaString(Surface *dst, const Common::String &str, int x, int y, int w, uint32 color, TextAlign align = kTextAlignLeft, int deltax = 0, bool useEllipsis = true) const;
- Graphics::Surface *surface = new Graphics::Surface();
- surface->create((uint16)width, (uint16)(_lineHeight * lines.size()), _game->_renderer->getPixelFormat());
+ // TODO: _isUnderline, _isBold, _isItalic, _isStriked
+
uint32 useColor = 0xffffffff;
- Common::Array<WideString>::iterator it;
int heightOffset = 0;
for (it = lines.begin(); it != lines.end(); ++it) {
- WideString str;
+ TextLine *line = (*it);
+ WideString str, lineStr = line->getText();
if (_game->_textRTL) {
- str = Common::convertBiDiU32String(*it, Common::BIDI_PAR_RTL);
+ str = Common::convertBiDiU32String(lineStr, Common::BIDI_PAR_RTL);
} else {
- str = Common::convertBiDiU32String(*it, Common::BIDI_PAR_LTR);
+ str = Common::convertBiDiU32String(lineStr, Common::BIDI_PAR_LTR);
}
_font->drawAlphaString(surface, str, 0, heightOffset, width, useColor, alignment);
heightOffset += (int)_lineHeight;
@@ -279,7 +290,6 @@ BaseSurface *BaseFontTT::renderTextToTexture(const WideString &text, int width,
surface->free();
delete surface;
return retSurface;
- // TODO: _isUnderline, _isBold, _isItalic, _isStriked
}
@@ -680,35 +690,130 @@ bool BaseFontTT::initFont() {
}
//////////////////////////////////////////////////////////////////////////
-void BaseFontTT::measureText(const WideString &text, int maxWidth, int maxHeight, int &textWidth, int &textHeight) {
- //TextLineList lines;
-
- if (maxWidth >= 0) {
- Common::Array<WideString> lines;
- _font->wordWrapText(text, maxWidth, lines);
- Common::Array<WideString>::iterator it;
- textWidth = 0;
- for (it = lines.begin(); it != lines.end(); ++it) {
- if (!it)
- continue;
- textWidth = MAX(textWidth, _font->getStringWidth(*it));
+int32 BaseFontTT::wrapText(const WideString &text, int32 maxWidth, int32 maxHeight, TextLineList &lines) {
+ int32 currWidth = 0;
+ wchar_t prevChar = L'\0';
+ int32 prevSpaceIndex = -1;
+ int32 prevSpaceWidth = 0;
+ int32 lineStartIndex = 0;
+
+ for (size_t i = 0; i < text.size(); i++) {
+ wchar_t ch = text[i];
+
+ /* remember the last space character in the string
+ * for wrapping the line later if necessary
+ */
+ if (ch == L' ') {
+ prevSpaceIndex = i;
+ prevSpaceWidth = currWidth;
}
- //WrapText(text, maxWidth, maxHeight, lines);
+ int32 charWidth = 0;
- textHeight = (int)(lines.size() * _lineHeight);
- } else {
- textWidth = _font->getStringWidth(text);
- textHeight = _fontHeight;
- }
- /*
- TextLineList::iterator it;
- for (it = lines.begin(); it != lines.end(); ++it) {
- TextLine *line = (*it);
- textWidth = MAX(textWidth, line->GetWidth());
- delete line;
- line = nullptr;
- }*/
+ /* measure width of this char
+ * (advanceX + kerning)
+ */
+ if (ch != L'\n') {
+ float kerning = 0;
+ if (prevChar != L'\0') {
+ kerning = getKerning(prevChar, ch);
+ }
+ prevChar = ch;
+
+ /* Small, but important difference! The computation of width must
+ * match the one from the rendering EXACTLY, including precision
+ * loss from casting. Otherwise, the bounds of the surface will
+ * be exceeded.
+ *
+ */
+ charWidth = (((int32)_font->getCharWidth(ch)) + ((int32)kerning));
+ }
+
+ bool lineTooLong = maxWidth >= 0 && currWidth + charWidth > maxWidth;
+ bool breakOnSpace = false;
+
+ // we can't fit even a single character
+ if (lineTooLong && currWidth == 0) {
+ break;
+ }
+
+ /* check if the text shall be wrapped
+ */
+ if (ch == L'\n' || i == text.size() - 1 || lineTooLong) {
+ int32 breakPoint, breakWidth;
+
+ if (prevSpaceIndex >= 0 && lineTooLong) {
+ /* we have a previous space character that we can wrap the text at */
+ breakPoint = prevSpaceIndex;
+ breakWidth = prevSpaceWidth;
+ breakOnSpace = true;
+ } else {
+ /* need to break at the current position */
+ breakPoint = i;
+ breakWidth = currWidth;
+
+ breakOnSpace = (ch == L'\n');
+
+ // we're at the end, so "consume" the last character as well
+ if (i == text.size() - 1) {
+ breakPoint++;
+ breakWidth += charWidth;
+ }
+ }
+
+ /* max. height exceeded --> "discard" this line and all following text
+ * i.e. do not add it to the text line list, return immediately
+ */
+ if (maxHeight >= 0 && (lines.size() + 1) * getLineHeight() > maxHeight) {
+ break;
+ }
+
+ WideString line = text.substr(lineStartIndex, breakPoint - lineStartIndex);
+ lines.push_back(new TextLine(line, breakWidth));
+
+ /* reset all values for the next line */
+ currWidth = 0;
+ prevChar = L'\0';
+ prevSpaceIndex = -1;
+
+ /* swallow (discard) spaces when breaking line */
+ if (breakOnSpace) {
+ breakPoint++;
+ }
+
+ lineStartIndex = breakPoint;
+ i = breakPoint - 1;
+
+ continue;
+ }
+
+ // if (ch == L' ' && currLine.empty()) continue;
+ currWidth += charWidth;
+ }
+
+ // return the height of the "accepted" text
+ return (lines.size() * getLineHeight());
+}
+
+//////////////////////////////////////////////////////////////////////////
+void BaseFontTT::measureText(const WideString &text, int32 maxWidth, int32 maxHeight, int32 &textWidth, int32 &textHeight) {
+ TextLineList lines;
+ wrapText(text, maxWidth, maxHeight, lines);
+
+ textHeight = (int32)(lines.size() * getLineHeight());
+ textWidth = 0;
+
+ TextLineList::iterator it;
+ for (it = lines.begin(); it != lines.end(); ++it) {
+ TextLine *line = (*it);
+ textWidth = MAX(textWidth, line->getWidth());
+ SAFE_DELETE(line);
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+float BaseFontTT::getKerning(wchar_t leftChar, wchar_t rightChar) {
+ return _font->getKerningOffset(leftChar, rightChar);
}
} // End of namespace Wintermute
diff --git a/engines/wintermute/base/font/base_font_truetype.h b/engines/wintermute/base/font/base_font_truetype.h
index 4d05a0e65e7..f379b4ef076 100644
--- a/engines/wintermute/base/font/base_font_truetype.h
+++ b/engines/wintermute/base/font/base_font_truetype.h
@@ -94,6 +94,27 @@ public:
uint32 _color;
};
+ //////////////////////////////////////////////////////////////////////////
+ class TextLine {
+ public:
+ TextLine(const WideString &text, int32 width) {
+ _text = text;
+ _width = width;
+ }
+
+ const WideString &getText() const {
+ return _text;
+ }
+ int32 getWidth() const {
+ return _width;
+ }
+
+ private:
+ WideString _text;
+ int32 _width;
+ };
+ typedef Common::List<TextLine *> TextLineList;
+
public:
DECLARE_PERSISTENT(BaseFontTT, BaseFont)
BaseFontTT(BaseGame *inGame);
@@ -107,13 +128,19 @@ public:
bool loadBuffer(char *buffer);
bool loadFile(const char *filename);
+ float getLineHeight() const {
+ return _lineHeight;
+ }
+
void afterLoad() override;
void initLoop() override;
private:
bool parseLayer(BaseTTFontLayer *layer, char *buffer);
+ int32 wrapText(const WideString &text, int32 maxWidth, int32 maxHeight, TextLineList &lines);
void measureText(const WideString &text, int maxWidth, int maxHeight, int &textWidth, int &textHeight);
+ float getKerning(wchar_t leftChar, wchar_t rightChar);
BaseSurface *renderTextToTexture(const WideString &text, int width, TTextAlign align, int maxHeight, int &textOffset);
More information about the Scummvm-git-logs
mailing list