diff options
-rw-r--r-- | graphics/font.cpp | 40 | ||||
-rw-r--r-- | graphics/font.h | 9 | ||||
-rw-r--r-- | graphics/fonts/ttf.cpp | 37 | ||||
-rw-r--r-- | gui/ThemeEngine.cpp | 4 | ||||
-rw-r--r-- | gui/ThemeEngine.h | 2 | ||||
-rw-r--r-- | gui/gui-manager.h | 1 | ||||
-rw-r--r-- | gui/widgets/editable.cpp | 11 | ||||
-rw-r--r-- | gui/widgets/edittext.cpp | 5 |
8 files changed, 92 insertions, 17 deletions
diff --git a/graphics/font.cpp b/graphics/font.cpp index 3f7152a95e..8775bccc7b 100644 --- a/graphics/font.cpp +++ b/graphics/font.cpp @@ -26,11 +26,20 @@ namespace Graphics { +int Font::getKerningOffset(byte left, byte right) const { + return 0; +} + int Font::getStringWidth(const Common::String &str) const { int space = 0; + uint last = 0; + + for (uint i = 0; i < str.size(); ++i) { + const uint cur = str[i]; + space += getCharWidth(cur) + getKerningOffset(last, cur); + last = cur; + } - for (uint i = 0; i < str.size(); ++i) - space += getCharWidth(str[i]); return space; } @@ -65,17 +74,22 @@ void Font::drawString(Surface *dst, const Common::String &sOld, int x, int y, in // for now. const int halfWidth = (w - ellipsisWidth) / 2; int w2 = 0; + uint last = 0; for (i = 0; i < s.size(); ++i) { - int charWidth = getCharWidth(s[i]); + const uint cur = s[i]; + int charWidth = getCharWidth(cur) + getKerningOffset(last, cur); if (w2 + charWidth > halfWidth) break; + last = cur; w2 += charWidth; - str += s[i]; + str += cur; } + // At this point we know that the first 'i' chars are together 'w2' // pixels wide. We took the first i-1, and add "..." to them. str += "..."; + last = '.'; // The original string is width wide. Of those we already skipped past // w2 pixels, which means (width - w2) remain. @@ -85,7 +99,9 @@ void Font::drawString(Surface *dst, const Common::String &sOld, int x, int y, in // (width + ellipsisWidth - w) int skip = width + ellipsisWidth - w; for (; i < s.size() && skip > 0; ++i) { - skip -= getCharWidth(s[i]); + const uint cur = s[i]; + skip -= getCharWidth(cur) + getKerningOffset(last, cur); + last = cur; } // Append the remaining chars, if any @@ -104,8 +120,12 @@ void Font::drawString(Surface *dst, const Common::String &sOld, int x, int y, in x = x + w - width; x += deltax; + uint last = 0; for (i = 0; i < str.size(); ++i) { - w = getCharWidth(str[i]); + const uint cur = str[i]; + x += getKerningOffset(last, cur); + last = cur; + w = getCharWidth(cur); if (x+w > rightX) break; if (x >= leftX) @@ -153,9 +173,11 @@ int Font::wordWrapText(const Common::String &str, int maxWidth, Common::Array<Co // of a line. If we encounter such a word, we have to wrap it over multiple // lines. + uint last = 0; for (Common::String::const_iterator x = str.begin(); x != str.end(); ++x) { const byte c = *x; - const int w = getCharWidth(c); + const int w = getCharWidth(c) + getKerningOffset(last, c); + last = c; const bool wouldExceedWidth = (lineWidth + tmpWidth + w > maxWidth); // If this char is a whitespace, then it represents a potential @@ -187,8 +209,10 @@ int Font::wordWrapText(const Common::String &str, int maxWidth, Common::Array<Co wrapper.add(line, lineWidth); // Trim left side while (tmpStr.size() && isspace(static_cast<unsigned char>(tmpStr[0]))) { - tmpWidth -= getCharWidth(tmpStr[0]); tmpStr.deleteChar(0); + // This is not very fast, but it is the simplest way to + // assure we do not mess something up because of kerning. + tmpWidth = getStringWidth(tmpStr); } } else { wrapper.add(tmpStr, tmpWidth); diff --git a/graphics/font.h b/graphics/font.h index 9c0b4affc1..6819b42f52 100644 --- a/graphics/font.h +++ b/graphics/font.h @@ -73,6 +73,15 @@ public: virtual int getCharWidth(byte chr) const = 0; /** + * Query the kerning offset between two characters. + * + * @param left The left character. May be 0. + * @param right The right character. May be 0. + * @return The horizontal displacement. + */ + virtual int getKerningOffset(byte left, byte right) const; + + /** * Draw a character at a specific point on a surface. * * Note that the point describes the top left edge point of the diff --git a/graphics/fonts/ttf.cpp b/graphics/fonts/ttf.cpp index 8a6b87c000..b5bf5c0158 100644 --- a/graphics/fonts/ttf.cpp +++ b/graphics/fonts/ttf.cpp @@ -109,6 +109,8 @@ public: virtual int getCharWidth(byte chr) const; + virtual int getKerningOffset(byte left, byte right) const; + virtual void drawChar(Surface *dst, byte chr, int x, int y, uint32 color) const; private: bool _initialized; @@ -126,16 +128,19 @@ private: int advance; }; - bool cacheGlyph(Glyph &glyph, byte chr); + bool cacheGlyph(Glyph &glyph, FT_UInt &slot, uint chr); typedef Common::HashMap<byte, Glyph> GlyphCache; GlyphCache _glyphs; + FT_UInt _glyphSlots[256]; + bool _monochrome; + bool _hasKerning; }; TTFFont::TTFFont() : _initialized(false), _face(), _ttfFile(0), _size(0), _width(0), _height(0), _ascent(0), - _descent(0), _glyphs(), _monochrome(false) { + _descent(0), _glyphs(), _glyphSlots(), _monochrome(false), _hasKerning(false) { } TTFFont::~TTFFont() { @@ -187,6 +192,9 @@ bool TTFFont::load(Common::SeekableReadStream &stream, int size, bool monochrome return false; } + // Check whether we have kerning support + _hasKerning = (FT_HAS_KERNING(_face) != 0); + if (FT_Set_Char_Size(_face, 0, size * 64, 0, 0)) { delete[] _ttfFile; _ttfFile = 0; @@ -204,8 +212,10 @@ bool TTFFont::load(Common::SeekableReadStream &stream, int size, bool monochrome _height = _ascent - _descent + 1; // Load all ISO-8859-1 characters. - for (uint i = 0; i < 256; ++i) - cacheGlyph(_glyphs[i], i); + for (uint i = 0; i < 256; ++i) { + if (!cacheGlyph(_glyphs[i], _glyphSlots[i], i)) + _glyphSlots[i] = 0; + } _initialized = (_glyphs.size() != 0); return _initialized; @@ -227,6 +237,21 @@ int TTFFont::getCharWidth(byte chr) const { return glyphEntry->_value.advance; } +int TTFFont::getKerningOffset(byte left, byte right) const { + if (!_hasKerning) + return 0; + + FT_UInt leftGlyph = _glyphSlots[left]; + FT_UInt rightGlyph = _glyphSlots[right]; + + if (!leftGlyph || !rightGlyph) + return 0; + + FT_Vector kerningVector; + FT_Get_Kerning(_face, leftGlyph, rightGlyph, FT_KERNING_DEFAULT, &kerningVector); + return (kerningVector.x / 64); +} + namespace { template<typename ColorType> @@ -336,8 +361,8 @@ void TTFFont::drawChar(Surface *dst, byte chr, int x, int y, uint32 color) const } } -bool TTFFont::cacheGlyph(Glyph &glyph, byte chr) { - FT_UInt slot = FT_Get_Char_Index(_face, chr); +bool TTFFont::cacheGlyph(Glyph &glyph, FT_UInt &slot, uint chr) { + slot = FT_Get_Char_Index(_face, chr); if (!slot) return false; diff --git a/gui/ThemeEngine.cpp b/gui/ThemeEngine.cpp index 585789b515..46a0eda0c5 100644 --- a/gui/ThemeEngine.cpp +++ b/gui/ThemeEngine.cpp @@ -1373,6 +1373,10 @@ int ThemeEngine::getCharWidth(byte c, FontStyle font) const { return ready() ? _texts[fontStyleToData(font)]->_fontPtr->getCharWidth(c) : 0; } +int ThemeEngine::getKerningOffset(byte left, byte right, FontStyle font) const { + return ready() ? _texts[fontStyleToData(font)]->_fontPtr->getKerningOffset(left, right) : 0; +} + TextData ThemeEngine::getTextData(DrawData ddId) const { return _widgets[ddId] ? (TextData)_widgets[ddId]->_textDataId : kTextDataNone; } diff --git a/gui/ThemeEngine.h b/gui/ThemeEngine.h index c49952ba0f..f041f85ab9 100644 --- a/gui/ThemeEngine.h +++ b/gui/ThemeEngine.h @@ -310,6 +310,8 @@ public: int getCharWidth(byte c, FontStyle font = kFontStyleBold) const; + int getKerningOffset(byte left, byte right, FontStyle font = kFontStyleBold) const; + //@} diff --git a/gui/gui-manager.h b/gui/gui-manager.h index addd2e6611..49542fd001 100644 --- a/gui/gui-manager.h +++ b/gui/gui-manager.h @@ -81,6 +81,7 @@ public: int getFontHeight(ThemeEngine::FontStyle style = ThemeEngine::kFontStyleBold) const { return _theme->getFontHeight(style); } int getStringWidth(const Common::String &str, ThemeEngine::FontStyle style = ThemeEngine::kFontStyleBold) const { return _theme->getStringWidth(str, style); } int getCharWidth(byte c, ThemeEngine::FontStyle style = ThemeEngine::kFontStyleBold) const { return _theme->getCharWidth(c, style); } + int getKerningOffset(byte left, byte right, ThemeEngine::FontStyle font = ThemeEngine::kFontStyleBold) const { return _theme->getKerningOffset(left, right, font); } /** * Tell the GuiManager to check whether the screen resolution has changed. diff --git a/gui/widgets/editable.cpp b/gui/widgets/editable.cpp index 4a0ee54828..6fae9346b2 100644 --- a/gui/widgets/editable.cpp +++ b/gui/widgets/editable.cpp @@ -238,8 +238,13 @@ void EditableWidget::defaultKeyDownHandler(Common::KeyState &state, bool &dirty, int EditableWidget::getCaretOffset() const { int caretpos = 0; - for (int i = 0; i < _caretPos; i++) - caretpos += g_gui.getCharWidth(_editString[i], _font); + + uint last = 0; + for (int i = 0; i < _caretPos; ++i) { + const uint cur = _editString[i]; + caretpos += g_gui.getCharWidth(cur, _font) + g_gui.getKerningOffset(last, cur, _font); + last = cur; + } caretpos -= _editScrollOffset; @@ -270,6 +275,8 @@ void EditableWidget::drawCaret(bool erase) { if ((uint)_caretPos < _editString.size()) { GUI::EditableWidget::String chr(_editString[_caretPos]); int chrWidth = g_gui.getCharWidth(_editString[_caretPos], _font); + const uint last = (_caretPos > 0) ? _editString[_caretPos - 1] : 0; + x += g_gui.getKerningOffset(last, _editString[_caretPos], _font); g_gui.theme()->drawText(Common::Rect(x, y, x + chrWidth, y + editRect.height() - 2), chr, _state, Graphics::kTextAlignLeft, _inversion, 0, false, _font); } } diff --git a/gui/widgets/edittext.cpp b/gui/widgets/edittext.cpp index 0337fe1e87..af8ab8aa5b 100644 --- a/gui/widgets/edittext.cpp +++ b/gui/widgets/edittext.cpp @@ -67,10 +67,13 @@ void EditTextWidget::handleMouseDown(int x, int y, int button, int clickCount) { int width = 0; uint i; + uint last = 0; for (i = 0; i < _editString.size(); ++i) { - width += g_gui.theme()->getCharWidth(_editString[i], _font); + const uint cur = _editString[i]; + width += g_gui.getCharWidth(cur, _font) + g_gui.getKerningOffset(last, cur, _font); if (width >= x) break; + last = cur; } if (setCaretPos(i)) draw(); |