diff options
Diffstat (limited to 'graphics')
-rw-r--r-- | graphics/font.cpp | 217 | ||||
-rw-r--r-- | graphics/font.h | 10 | ||||
-rw-r--r-- | graphics/fonts/bdf.cpp | 8 | ||||
-rw-r--r-- | graphics/fonts/bdf.h | 6 | ||||
-rw-r--r-- | graphics/fonts/ttf.cpp | 81 | ||||
-rw-r--r-- | graphics/fonts/winfont.cpp | 6 | ||||
-rw-r--r-- | graphics/fonts/winfont.h | 6 | ||||
-rw-r--r-- | graphics/surface.h | 4 |
8 files changed, 209 insertions, 129 deletions
diff --git a/graphics/font.cpp b/graphics/font.cpp index a852274b06..e9f139c478 100644 --- a/graphics/font.cpp +++ b/graphics/font.cpp @@ -26,93 +26,32 @@ namespace Graphics { -int Font::getKerningOffset(byte left, byte right) const { +int Font::getKerningOffset(uint32 left, uint32 right) const { return 0; } -int Font::getStringWidth(const Common::String &str) const { +namespace { + +template<class StringType> +int getStringWidthImpl(const Font &font, const StringType &str) { int space = 0; - uint last = 0; + typename StringType::unsigned_type last = 0; for (uint i = 0; i < str.size(); ++i) { - const uint cur = str[i]; - space += getCharWidth(cur) + getKerningOffset(last, cur); + const typename StringType::unsigned_type cur = str[i]; + space += font.getCharWidth(cur) + font.getKerningOffset(last, cur); last = cur; } return space; } -void Font::drawString(Surface *dst, const Common::String &sOld, int x, int y, int w, uint32 color, TextAlign align, int deltax, bool useEllipsis) const { +template<class StringType> +void drawStringImpl(const Font &font, Surface *dst, const StringType &str, int x, int y, int w, uint32 color, TextAlign align, int deltax) { assert(dst != 0); - const int leftX = x, rightX = x + w; - uint i; - Common::String s = sOld; - int width = getStringWidth(s); - Common::String str; - - if (useEllipsis && width > w && s.hasSuffix("...")) { - // String is too wide. Check whether it ends in an ellipsis - // ("..."). If so, remove that and try again! - s.deleteLastChar(); - s.deleteLastChar(); - s.deleteLastChar(); - width = getStringWidth(s); - } - - if (useEllipsis && width > w) { - // String is too wide. So we shorten it "intelligently" by - // replacing parts of the string by an ellipsis. There are - // three possibilities for this: replace the start, the end, or - // the middle of the string. What is best really depends on the - // context; but unless we want to make this configurable, - // replacing the middle seems to be a good compromise. - - const int ellipsisWidth = getStringWidth("..."); - - // SLOW algorithm to remove enough of the middle. But it is good enough - // for now. - const int halfWidth = (w - ellipsisWidth) / 2; - int w2 = 0; - uint last = 0; - - for (i = 0; i < s.size(); ++i) { - const uint cur = s[i]; - int charWidth = getCharWidth(cur) + getKerningOffset(last, cur); - if (w2 + charWidth > halfWidth) - break; - last = cur; - w2 += charWidth; - 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. - // The new str is (w2+ellipsisWidth) wide, so we can accommodate about - // (w - (w2+ellipsisWidth)) more pixels. - // Thus we skip ((width - w2) - (w - (w2+ellipsisWidth))) = - // (width + ellipsisWidth - w) - int skip = width + ellipsisWidth - w; - for (; i < s.size() && skip > 0; ++i) { - const uint cur = s[i]; - skip -= getCharWidth(cur) + getKerningOffset(last, cur); - last = cur; - } - - // Append the remaining chars, if any - for (; i < s.size(); ++i) { - str += s[i]; - } - - width = getStringWidth(str); - } else { - str = s; - } + const int leftX = x, rightX = x + w; + int width = font.getStringWidth(str); if (align == kTextAlignCenter) x = x + (w - width)/2; @@ -120,29 +59,29 @@ 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) { - const uint cur = str[i]; - x += getKerningOffset(last, cur); + typename StringType::unsigned_type last = 0; + for (typename StringType::const_iterator i = str.begin(), end = str.end(); i != end; ++i) { + const typename StringType::unsigned_type cur = *i; + x += font.getKerningOffset(last, cur); last = cur; - w = getCharWidth(cur); + w = font.getCharWidth(cur); if (x+w > rightX) break; if (x+w >= leftX) - drawChar(dst, str[i], x, y, color); + font.drawChar(dst, cur, x, y, color); x += w; } } - +template<class StringType> struct WordWrapper { - Common::Array<Common::String> &lines; + Common::Array<StringType> &lines; int actualMaxLineWidth; - WordWrapper(Common::Array<Common::String> &l) : lines(l), actualMaxLineWidth(0) { + WordWrapper(Common::Array<StringType> &l) : lines(l), actualMaxLineWidth(0) { } - void add(Common::String &line, int &w) { + void add(StringType &line, int &w) { if (actualMaxLineWidth < w) actualMaxLineWidth = w; @@ -153,10 +92,11 @@ struct WordWrapper { } }; -int Font::wordWrapText(const Common::String &str, int maxWidth, Common::Array<Common::String> &lines) const { - WordWrapper wrapper(lines); - Common::String line; - Common::String tmpStr; +template<class StringType> +int wordWrapTextImpl(const Font &font, const StringType &str, int maxWidth, Common::Array<StringType> &lines) { + WordWrapper<StringType> wrapper(lines); + StringType line; + StringType tmpStr; int lineWidth = 0; int tmpWidth = 0; @@ -173,10 +113,10 @@ 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) + getKerningOffset(last, c); + typename StringType::unsigned_type last = 0; + for (typename StringType::const_iterator x = str.begin(); x != str.end(); ++x) { + const typename StringType::unsigned_type c = *x; + const int w = font.getCharWidth(c) + font.getKerningOffset(last, c); last = c; const bool wouldExceedWidth = (lineWidth + tmpWidth + w > maxWidth); @@ -212,7 +152,7 @@ int Font::wordWrapText(const Common::String &str, int maxWidth, Common::Array<Co 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); + tmpWidth = font.getStringWidth(tmpStr); } } else { wrapper.add(tmpStr, tmpWidth); @@ -232,5 +172,98 @@ int Font::wordWrapText(const Common::String &str, int maxWidth, Common::Array<Co return wrapper.actualMaxLineWidth; } +} // End of anonymous namespace + +int Font::getStringWidth(const Common::String &str) const { + return getStringWidthImpl(*this, str); +} + +int Font::getStringWidth(const Common::U32String &str) const { + return getStringWidthImpl(*this, str); +} + +void Font::drawString(Surface *dst, const Common::String &sOld, int x, int y, int w, uint32 color, TextAlign align, int deltax, bool useEllipsis) const { + Common::String s = sOld; + int width = getStringWidth(s); + Common::String str; + + if (useEllipsis && width > w && s.hasSuffix("...")) { + // String is too wide. Check whether it ends in an ellipsis + // ("..."). If so, remove that and try again! + s.deleteLastChar(); + s.deleteLastChar(); + s.deleteLastChar(); + width = getStringWidth(s); + } + + if (useEllipsis && width > w) { + // String is too wide. So we shorten it "intelligently" by + // replacing parts of the string by an ellipsis. There are + // three possibilities for this: replace the start, the end, or + // the middle of the string. What is best really depends on the + // context; but unless we want to make this configurable, + // replacing the middle seems to be a good compromise. + + const int ellipsisWidth = getStringWidth("..."); + + // SLOW algorithm to remove enough of the middle. But it is good enough + // for now. + const int halfWidth = (w - ellipsisWidth) / 2; + int w2 = 0; + Common::String::unsigned_type last = 0; + uint i; + + for (i = 0; i < s.size(); ++i) { + const Common::String::unsigned_type cur = s[i]; + int charWidth = getCharWidth(cur) + getKerningOffset(last, cur); + if (w2 + charWidth > halfWidth) + break; + last = cur; + w2 += charWidth; + 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. + // The new str is (w2+ellipsisWidth) wide, so we can accommodate about + // (w - (w2+ellipsisWidth)) more pixels. + // Thus we skip ((width - w2) - (w - (w2+ellipsisWidth))) = + // (width + ellipsisWidth - w) + int skip = width + ellipsisWidth - w; + for (; i < s.size() && skip > 0; ++i) { + const Common::String::unsigned_type cur = s[i]; + skip -= getCharWidth(cur) + getKerningOffset(last, cur); + last = cur; + } + + // Append the remaining chars, if any + for (; i < s.size(); ++i) { + str += s[i]; + } + + width = getStringWidth(str); + } else { + str = s; + } + + drawStringImpl(*this, dst, str, x, y, w, color, align, deltax); +} + +void Font::drawString(Surface *dst, const Common::U32String &str, int x, int y, int w, uint32 color, TextAlign align) const { + drawStringImpl(*this, dst, str, x, y, w, color, align, 0); +} + +int Font::wordWrapText(const Common::String &str, int maxWidth, Common::Array<Common::String> &lines) const { + return wordWrapTextImpl(*this, str, maxWidth, lines); +} + +int Font::wordWrapText(const Common::U32String &str, int maxWidth, Common::Array<Common::U32String> &lines) const { + return wordWrapTextImpl(*this, str, maxWidth, lines); +} } // End of namespace Graphics diff --git a/graphics/font.h b/graphics/font.h index 6819b42f52..77b7623a85 100644 --- a/graphics/font.h +++ b/graphics/font.h @@ -23,6 +23,7 @@ #define GRAPHICS_FONT_H #include "common/str.h" +#include "common/ustr.h" namespace Common { template<class T> class Array; @@ -70,7 +71,7 @@ public: * @param chr The character to query the width of. * @return The character's width. */ - virtual int getCharWidth(byte chr) const = 0; + virtual int getCharWidth(uint32 chr) const = 0; /** * Query the kerning offset between two characters. @@ -79,7 +80,7 @@ public: * @param right The right character. May be 0. * @return The horizontal displacement. */ - virtual int getKerningOffset(byte left, byte right) const; + virtual int getKerningOffset(uint32 left, uint32 right) const; /** * Draw a character at a specific point on a surface. @@ -96,15 +97,17 @@ public: * @param y The y coordinate where to draw the character. * @param color The color of the character. */ - virtual void drawChar(Surface *dst, byte chr, int x, int y, uint32 color) const = 0; + virtual void drawChar(Surface *dst, uint32 chr, int x, int y, uint32 color) const = 0; // TODO: Add doxygen comments to this void drawString(Surface *dst, const Common::String &str, int x, int y, int w, uint32 color, TextAlign align = kTextAlignLeft, int deltax = 0, bool useEllipsis = true) const; + void drawString(Surface *dst, const Common::U32String &str, int x, int y, int w, uint32 color, TextAlign align = kTextAlignLeft) const; /** * Compute and return the width the string str has when rendered using this font. */ int getStringWidth(const Common::String &str) const; + int getStringWidth(const Common::U32String &str) const; /** * Take a text (which may contain newline characters) and word wrap it so that @@ -120,6 +123,7 @@ public: * @return the maximal width of any of the lines added to lines */ int wordWrapText(const Common::String &str, int maxWidth, Common::Array<Common::String> &lines) const; + int wordWrapText(const Common::U32String &str, int maxWidth, Common::Array<Common::U32String> &lines) const; }; } // End of namespace Graphics diff --git a/graphics/fonts/bdf.cpp b/graphics/fonts/bdf.cpp index e523a36ad5..7b2290cc3a 100644 --- a/graphics/fonts/bdf.cpp +++ b/graphics/fonts/bdf.cpp @@ -51,7 +51,7 @@ int BdfFont::getMaxCharWidth() const { return _data.maxAdvance; } -int BdfFont::getCharWidth(byte chr) const { +int BdfFont::getCharWidth(uint32 chr) const { // In case all font have the same advance value, we use the maximum. if (!_data.advances) return _data.maxAdvance; @@ -85,9 +85,9 @@ void drawCharIntern(byte *ptr, uint pitch, const byte *src, int h, int width, in } } -int BdfFont::mapToIndex(byte ch) const { +int BdfFont::mapToIndex(uint32 ch) const { // Check whether the character is included - if (_data.firstCharacter <= ch && ch <= _data.firstCharacter + _data.numCharacters) { + if (_data.firstCharacter <= (int)ch && (int)ch <= _data.firstCharacter + _data.numCharacters) { if (_data.bitmaps[ch - _data.firstCharacter]) return ch - _data.firstCharacter; } @@ -95,7 +95,7 @@ int BdfFont::mapToIndex(byte ch) const { return _data.defaultCharacter - _data.firstCharacter; } -void BdfFont::drawChar(Surface *dst, byte chr, const int tx, const int ty, const uint32 color) const { +void BdfFont::drawChar(Surface *dst, uint32 chr, const int tx, const int ty, const uint32 color) const { assert(dst != 0); // TODO: Where is the relation between the max advance being smaller or diff --git a/graphics/fonts/bdf.h b/graphics/fonts/bdf.h index b0166a2095..842e54f851 100644 --- a/graphics/fonts/bdf.h +++ b/graphics/fonts/bdf.h @@ -61,14 +61,14 @@ public: virtual int getFontHeight() const; virtual int getMaxCharWidth() const; - virtual int getCharWidth(byte chr) const; - virtual void drawChar(Surface *dst, byte chr, int x, int y, uint32 color) const; + virtual int getCharWidth(uint32 chr) const; + virtual void drawChar(Surface *dst, uint32 chr, int x, int y, uint32 color) const; static BdfFont *loadFont(Common::SeekableReadStream &stream); static bool cacheFontData(const BdfFont &font, const Common::String &filename); static BdfFont *loadFromCache(Common::SeekableReadStream &stream); private: - int mapToIndex(byte ch) const; + int mapToIndex(uint32 ch) const; const BdfFontData _data; const DisposeAfterUse::Flag _dispose; diff --git a/graphics/fonts/ttf.cpp b/graphics/fonts/ttf.cpp index b9e9610d77..93f119f028 100644 --- a/graphics/fonts/ttf.cpp +++ b/graphics/fonts/ttf.cpp @@ -107,11 +107,11 @@ public: virtual int getMaxCharWidth() const; - virtual int getCharWidth(byte chr) const; + virtual int getCharWidth(uint32 chr) const; - virtual int getKerningOffset(byte left, byte right) const; + virtual int getKerningOffset(uint32 left, uint32 right) const; - virtual void drawChar(Surface *dst, byte chr, int x, int y, uint32 color) const; + virtual void drawChar(Surface *dst, uint32 chr, int x, int y, uint32 color) const; private: bool _initialized; FT_Face _face; @@ -126,13 +126,14 @@ private: Surface image; int xOffset, yOffset; int advance; + FT_UInt slot; }; - bool cacheGlyph(Glyph &glyph, FT_UInt &slot, uint chr); - typedef Common::HashMap<byte, Glyph> GlyphCache; - GlyphCache _glyphs; - - FT_UInt _glyphSlots[256]; + bool cacheGlyph(Glyph &glyph, uint32 chr) const; + typedef Common::HashMap<uint32, Glyph> GlyphCache; + mutable GlyphCache _glyphs; + bool _allowLateCaching; + void assureCached(uint32 chr) const; bool _monochrome; bool _hasKerning; @@ -140,7 +141,7 @@ private: TTFFont::TTFFont() : _initialized(false), _face(), _ttfFile(0), _size(0), _width(0), _height(0), _ascent(0), - _descent(0), _glyphs(), _glyphSlots(), _monochrome(false), _hasKerning(false) { + _descent(0), _glyphs(), _monochrome(false), _hasKerning(false), _allowLateCaching(false) { } TTFFont::~TTFFont() { @@ -212,19 +213,26 @@ bool TTFFont::load(Common::SeekableReadStream &stream, int size, uint dpi, bool _height = _ascent - _descent + 1; if (!mapping) { + // Allow loading of all unicode characters. + _allowLateCaching = true; + // Load all ISO-8859-1 characters. for (uint i = 0; i < 256; ++i) { - if (!cacheGlyph(_glyphs[i], _glyphSlots[i], i)) - _glyphSlots[i] = 0; + if (!cacheGlyph(_glyphs[i], i)) { + _glyphs.erase(i); + } } } else { + // We have a fixed map of characters do not load more later. + _allowLateCaching = false; + for (uint i = 0; i < 256; ++i) { const uint32 unicode = mapping[i] & 0x7FFFFFFF; const bool isRequired = (mapping[i] & 0x80000000) != 0; // Check whether loading an important glyph fails and error out if // that is the case. - if (!cacheGlyph(_glyphs[i], _glyphSlots[i], unicode)) { - _glyphSlots[i] = 0; + if (!cacheGlyph(_glyphs[i], unicode)) { + _glyphs.erase(i); if (isRequired) return false; } @@ -243,7 +251,8 @@ int TTFFont::getMaxCharWidth() const { return _width; } -int TTFFont::getCharWidth(byte chr) const { +int TTFFont::getCharWidth(uint32 chr) const { + assureCached(chr); GlyphCache::const_iterator glyphEntry = _glyphs.find(chr); if (glyphEntry == _glyphs.end()) return 0; @@ -251,12 +260,29 @@ int TTFFont::getCharWidth(byte chr) const { return glyphEntry->_value.advance; } -int TTFFont::getKerningOffset(byte left, byte right) const { +int TTFFont::getKerningOffset(uint32 left, uint32 right) const { if (!_hasKerning) return 0; - FT_UInt leftGlyph = _glyphSlots[left]; - FT_UInt rightGlyph = _glyphSlots[right]; + assureCached(left); + assureCached(right); + + FT_UInt leftGlyph, rightGlyph; + GlyphCache::const_iterator glyphEntry; + + glyphEntry = _glyphs.find(left); + if (glyphEntry != _glyphs.end()) { + leftGlyph = glyphEntry->_value.slot; + } else { + return 0; + } + + glyphEntry = _glyphs.find(right); + if (glyphEntry != _glyphs.end()) { + rightGlyph = glyphEntry->_value.slot; + } else { + return 0; + } if (!leftGlyph || !rightGlyph) return 0; @@ -304,7 +330,8 @@ void renderGlyph(uint8 *dstPos, const int dstPitch, const uint8 *srcPos, const i } // End of anonymous namespace -void TTFFont::drawChar(Surface *dst, byte chr, int x, int y, uint32 color) const { +void TTFFont::drawChar(Surface *dst, uint32 chr, int x, int y, uint32 color) const { + assureCached(chr); GlyphCache::const_iterator glyphEntry = _glyphs.find(chr); if (glyphEntry == _glyphs.end()) return; @@ -376,11 +403,13 @@ void TTFFont::drawChar(Surface *dst, byte chr, int x, int y, uint32 color) const } } -bool TTFFont::cacheGlyph(Glyph &glyph, FT_UInt &slot, uint chr) { - slot = FT_Get_Char_Index(_face, chr); +bool TTFFont::cacheGlyph(Glyph &glyph, uint32 chr) const { + FT_UInt slot = FT_Get_Char_Index(_face, chr); if (!slot) return false; + glyph.slot = slot; + // We use the light target and render mode to improve the looks of the // glyphs. It is most noticable in FreeSansBold.ttf, where otherwise the // 't' glyph looks like it is cut off on the right side. @@ -456,12 +485,24 @@ bool TTFFont::cacheGlyph(Glyph &glyph, FT_UInt &slot, uint chr) { default: warning("TTFFont::cacheGlyph: Unsupported pixel mode %d", bitmap.pixel_mode); + glyph.image.free(); return false; } return true; } +void TTFFont::assureCached(uint32 chr) const { + if (!chr || !_allowLateCaching || _glyphs.contains(chr)) { + return; + } + + Glyph newGlyph; + if (cacheGlyph(newGlyph, chr)) { + _glyphs[chr] = newGlyph; + } +} + Font *loadTTFFont(Common::SeekableReadStream &stream, int size, uint dpi, bool monochrome, const uint32 *mapping) { TTFFont *font = new TTFFont(); diff --git a/graphics/fonts/winfont.cpp b/graphics/fonts/winfont.cpp index 3bad92236d..c0ebab19ba 100644 --- a/graphics/fonts/winfont.cpp +++ b/graphics/fonts/winfont.cpp @@ -200,7 +200,7 @@ char WinFont::indexToCharacter(uint16 index) const { return index + _firstChar; } -uint16 WinFont::characterToIndex(byte character) const { +uint16 WinFont::characterToIndex(uint32 character) const { // Go to the default character if we didn't find a mapping if (character < _firstChar || character > _lastChar) character = _defaultChar; @@ -208,7 +208,7 @@ uint16 WinFont::characterToIndex(byte character) const { return character - _firstChar; } -int WinFont::getCharWidth(byte chr) const { +int WinFont::getCharWidth(uint32 chr) const { return _glyphs[characterToIndex(chr)].charWidth; } @@ -324,7 +324,7 @@ bool WinFont::loadFromFNT(Common::SeekableReadStream &stream) { return true; } -void WinFont::drawChar(Surface *dst, byte chr, int x, int y, uint32 color) const { +void WinFont::drawChar(Surface *dst, uint32 chr, int x, int y, uint32 color) const { assert(dst); assert(dst->format.bytesPerPixel == 1 || dst->format.bytesPerPixel == 2 || dst->format.bytesPerPixel == 4); assert(_glyphs); diff --git a/graphics/fonts/winfont.h b/graphics/fonts/winfont.h index 4382d7ed6b..2c7ba07b41 100644 --- a/graphics/fonts/winfont.h +++ b/graphics/fonts/winfont.h @@ -62,8 +62,8 @@ public: // Font API int getFontHeight() const { return _pixHeight; } int getMaxCharWidth() const { return _maxWidth; } - int getCharWidth(byte chr) const; - void drawChar(Surface *dst, byte chr, int x, int y, uint32 color) const; + int getCharWidth(uint32 chr) const; + void drawChar(Surface *dst, uint32 chr, int x, int y, uint32 color) const; private: bool loadFromPE(const Common::String &fileName, const WinFontDirEntry &dirEntry); @@ -72,7 +72,7 @@ private: uint32 getFontIndex(Common::SeekableReadStream &stream, const WinFontDirEntry &dirEntry); bool loadFromFNT(Common::SeekableReadStream &stream); char indexToCharacter(uint16 index) const; - uint16 characterToIndex(byte character) const; + uint16 characterToIndex(uint32 character) const; uint16 _pixHeight; uint16 _maxWidth; diff --git a/graphics/surface.h b/graphics/surface.h index 07e289b0bb..f1b2aa64ab 100644 --- a/graphics/surface.h +++ b/graphics/surface.h @@ -334,7 +334,9 @@ public: */ struct SharedPtrSurfaceDeleter { void operator()(Surface *ptr) { - ptr->free(); + if (ptr) { + ptr->free(); + } delete ptr; } }; |