aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJohannes Schickel2012-01-08 02:33:34 +0100
committerWillem Jan Palenstijn2012-01-29 16:26:20 +0100
commit9f3fbe1bd773664b1e86241e71875cd97230d791 (patch)
tree1eae5b18934a2433aef7205a86bdd9a999261346
parentd21ae1aa40f7ef5442d98c377800a0157af069c8 (diff)
downloadscummvm-rg350-9f3fbe1bd773664b1e86241e71875cd97230d791.tar.gz
scummvm-rg350-9f3fbe1bd773664b1e86241e71875cd97230d791.tar.bz2
scummvm-rg350-9f3fbe1bd773664b1e86241e71875cd97230d791.zip
GRAPHICS/GUI: Implement kerning support for Font.
This adapts the related graphics code, which is the generic Font API and the TTF font implementation. It furthermore adapts the GUI to properly take care of kerning in text input widgets.
-rw-r--r--graphics/font.cpp40
-rw-r--r--graphics/font.h9
-rw-r--r--graphics/fonts/ttf.cpp37
-rw-r--r--gui/ThemeEngine.cpp4
-rw-r--r--gui/ThemeEngine.h2
-rw-r--r--gui/gui-manager.h1
-rw-r--r--gui/widgets/editable.cpp11
-rw-r--r--gui/widgets/edittext.cpp5
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();