diff options
Diffstat (limited to 'graphics/font.cpp')
| -rw-r--r-- | graphics/font.cpp | 219 | 
1 files changed, 126 insertions, 93 deletions
| diff --git a/graphics/font.cpp b/graphics/font.cpp index 3b00cd8568..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 >= leftX) -			drawChar(dst, str[i], x, y, color); +		if (x+w >= leftX) +			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 | 
