diff options
Diffstat (limited to 'graphics/font.cpp')
-rw-r--r-- | graphics/font.cpp | 215 |
1 files changed, 124 insertions, 91 deletions
diff --git a/graphics/font.cpp b/graphics/font.cpp index 519d63a393..e9f139c478 100644 --- a/graphics/font.cpp +++ b/graphics/font.cpp @@ -30,89 +30,28 @@ 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 |