Changeset - r20613:8e39d5a2a85b
[Not reviewed]
master
0 6 0
michi_cc - 11 years ago 2013-08-05 20:35:23
michi_cc@openttd.org
(svn r25651) -Fix: Textbuf caret rendering for complex scripts (e.g. Tamil).
6 files changed with 105 insertions and 25 deletions:
0 comments (0 inline, 0 general)
src/gfx.cpp
Show inline comments
 
@@ -708,6 +708,20 @@ Dimension GetStringBoundingBox(StringID 
 
}
 

	
 
/**
 
 * Get the leading corner of a character in a single-line string relative
 
 * to the start of the string.
 
 * @param str String containing the character.
 
 * @param ch Pointer to the character in the string.
 
 * @param start_fontsize Font size to start the text with.
 
 * @return Upper left corner of the glyph associated with the character.
 
 */
 
Point GetCharPosInString(const char *str, const char *ch, FontSize start_fontsize)
 
{
 
	Layouter layout(str, INT32_MAX, TC_FROMSTRING, start_fontsize);
 
	return layout.GetCharPosition(ch);
 
}
 

	
 
/**
 
 * Draw single character horizontally centered around (x,y)
 
 * @param c           Character (glyph) to draw
 
 * @param x           X position to draw character
src/gfx_func.h
Show inline comments
 
@@ -126,6 +126,7 @@ int GetStringLineCount(StringID str, int
 
Dimension GetStringMultiLineBoundingBox(StringID str, const Dimension &suggestion);
 
Dimension GetStringMultiLineBoundingBox(const char *str, const Dimension &suggestion);
 
void LoadStringWidthTable(bool monospace = false);
 
Point GetCharPosInString(const char *str, const char *ch, FontSize start_fontsize = FS_NORMAL);
 

	
 
void DrawDirtyBlocks();
 
void SetDirtyBlocks(int left, int top, int right, int bottom);
src/gfx_layout.cpp
Show inline comments
 
@@ -423,7 +423,7 @@ ParagraphLayout *Layouter::GetParagraphL
 
 * @param colour   The colour of the font.
 
 * @param fontsize The size of font to use.
 
 */
 
Layouter::Layouter(const char *str, int maxw, TextColour colour, FontSize fontsize)
 
Layouter::Layouter(const char *str, int maxw, TextColour colour, FontSize fontsize) : string(str)
 
{
 
	FontState state(colour, fontsize);
 
	WChar c = 0;
 
@@ -513,6 +513,59 @@ Dimension Layouter::GetBounds()
 
}
 

	
 
/**
 
 * Get the position of a character in the layout.
 
 * @param ch Character to get the position of.
 
 * @return Upper left corner of the character relative to the start of the string.
 
 * @note Will only work right for single-line strings.
 
 */
 
Point Layouter::GetCharPosition(const char *ch) const
 
{
 
	/* Find the code point index which corresponds to the char
 
	 * pointer into our UTF-8 source string. */
 
	size_t index = 0;
 
	const char *str = this->string;
 
	while (str < ch) {
 
		WChar c;
 
		size_t len = Utf8Decode(&c, str);
 
		if (c == '\0' || c == '\n') break;
 
		str += len;
 
#ifdef WITH_ICU
 
		/* ICU uses UTF-16 internally which means we need to account for surrogate pairs. */
 
		index += len < 4 ? 1 : 2;
 
#else
 
		index++;
 
#endif
 
	}
 

	
 
	if (str == ch) {
 
		/* Valid character. */
 
		const ParagraphLayout::Line *line = *this->Begin();
 

	
 
		/* Pointer to the end-of-string/line marker? Return total line width. */
 
		if (*ch == '\0' || *ch == '\n') {
 
			Point p = { line->getWidth(), 0 };
 
			return p;
 
		}
 

	
 
		/* Scan all runs until we've found our code point index. */
 
		for (int run_index = 0; run_index < line->countRuns(); run_index++) {
 
			const ParagraphLayout::VisualRun *run = line->getVisualRun(run_index);
 

	
 
			for (int i = 0; i < run->getGlyphCount(); i++) {
 
				/* Matching glyph? Return position. */
 
				if ((size_t)run->getGlyphToCharMap()[i] == index) {
 
					Point p = { run->getPositions()[i * 2], run->getPositions()[i * 2 + 1] };
 
					return p;
 
				}
 
			}
 
		}
 
	}
 

	
 
	Point p = { 0, 0 };
 
	return p;
 
}
 

	
 
/**
 
 * Get a static font instance.
 
 */
 
Font *Layouter::GetFont(FontSize size, TextColour colour)
src/gfx_layout.h
Show inline comments
 
@@ -168,6 +168,8 @@ class Layouter : public AutoDeleteSmallV
 
	typedef WChar CharType; ///< The type of character used within the layouter.
 
#endif /* WITH_ICU */
 

	
 
	const char *string; ///< Pointer to the original string.
 

	
 
	size_t AppendToBuffer(CharType *buff, const CharType *buffer_last, WChar c);
 
	ParagraphLayout *GetParagraphLayout(CharType *buff, CharType *buff_end, FontMap &fontMapping);
 

	
 
@@ -209,6 +211,7 @@ class Layouter : public AutoDeleteSmallV
 
public:
 
	Layouter(const char *str, int maxw = INT32_MAX, TextColour colour = TC_FROMSTRING, FontSize fontsize = FS_NORMAL);
 
	Dimension GetBounds();
 
	Point GetCharPosition(const char *ch) const;
 

	
 
	static void ResetFontCache(FontSize size);
 
	static void ResetLineCache();
src/textbuf.cpp
Show inline comments
 
@@ -81,18 +81,17 @@ void Textbuf::DelChar(bool backspace)
 
	if (backspace) s = Utf8PrevChar(s);
 

	
 
	uint16 len = (uint16)Utf8Decode(&c, s);
 
	uint width = GetCharacterWidth(FS_NORMAL, c);
 

	
 
	this->pixels -= width;
 
	if (backspace) {
 
		this->caretpos   -= len;
 
		this->caretxoffs -= width;
 
	}
 

	
 
	/* Move the remaining characters over the marker */
 
	memmove(s, s + len, this->bytes - (s - this->buf) - len);
 
	this->bytes -= len;
 
	this->chars--;
 

	
 
	this->UpdateWidth();
 
	if (backspace) {
 
		this->caretpos -= len;
 
		this->UpdateCaretPosition();
 
	}
 
}
 

	
 
/**
 
@@ -159,17 +158,16 @@ void Textbuf::DeleteAll()
 
 */
 
bool Textbuf::InsertChar(WChar key)
 
{
 
	const byte charwidth = GetCharacterWidth(FS_NORMAL, key);
 
	uint16 len = (uint16)Utf8CharLen(key);
 
	if (this->bytes + len <= this->max_bytes && this->chars + 1 <= this->max_chars) {
 
		memmove(this->buf + this->caretpos + len, this->buf + this->caretpos, this->bytes - this->caretpos);
 
		Utf8Encode(this->buf + this->caretpos, key);
 
		this->chars++;
 
		this->bytes  += len;
 
		this->pixels += charwidth;
 
		this->UpdateWidth();
 

	
 
		this->caretpos   += len;
 
		this->caretxoffs += charwidth;
 
		this->UpdateCaretPosition();
 
		return true;
 
	}
 
	return false;
 
@@ -187,7 +185,7 @@ bool Textbuf::InsertClipboard()
 

	
 
	if (!GetClipboardContents(utf8_buf, lengthof(utf8_buf))) return false;
 

	
 
	uint16 pixels = 0, bytes = 0, chars = 0;
 
	uint16 bytes = 0, chars = 0;
 
	WChar c;
 
	for (const char *ptr = utf8_buf; (c = Utf8Consume(&ptr)) != '\0';) {
 
		if (!IsValidChar(c, this->afilter)) break;
 
@@ -196,9 +194,6 @@ bool Textbuf::InsertClipboard()
 
		if (this->bytes + bytes + len > this->max_bytes) break;
 
		if (this->chars + chars + 1   > this->max_chars) break;
 

	
 
		byte char_pixels = GetCharacterWidth(FS_NORMAL, c);
 

	
 
		pixels += char_pixels;
 
		bytes += len;
 
		chars++;
 
	}
 
@@ -207,8 +202,6 @@ bool Textbuf::InsertClipboard()
 

	
 
	memmove(this->buf + this->caretpos + bytes, this->buf + this->caretpos, this->bytes - this->caretpos);
 
	memcpy(this->buf + this->caretpos, utf8_buf, bytes);
 
	this->pixels += pixels;
 
	this->caretxoffs += pixels;
 

	
 
	this->bytes += bytes;
 
	this->chars += chars;
 
@@ -217,6 +210,9 @@ bool Textbuf::InsertClipboard()
 
	assert(this->chars <= this->max_chars);
 
	this->buf[this->bytes - 1] = '\0'; // terminating zero
 

	
 
	this->UpdateWidth();
 
	this->UpdateCaretPosition();
 

	
 
	return true;
 
}
 

	
 
@@ -242,7 +238,7 @@ WChar Textbuf::MoveCaretLeft()
 
	const char *s = Utf8PrevChar(this->buf + this->caretpos);
 
	Utf8Decode(&c, s);
 
	this->caretpos    = s - this->buf;
 
	this->caretxoffs -= GetCharacterWidth(FS_NORMAL, c);
 
	this->UpdateCaretPosition();
 

	
 
	return c;
 
}
 
@@ -267,12 +263,24 @@ WChar Textbuf::MoveCaretRight()
 

	
 
	WChar c;
 
	this->caretpos   += (uint16)Utf8Decode(&c, this->buf + this->caretpos);
 
	this->caretxoffs += GetCharacterWidth(FS_NORMAL, c);
 
	this->UpdateCaretPosition();
 

	
 
	Utf8Decode(&c, this->buf + this->caretpos);
 
	return c;
 
}
 

	
 
/** Update pixel width of the text. */
 
void Textbuf::UpdateWidth()
 
{
 
	this->pixels = GetStringBoundingBox(this->buf, FS_NORMAL).width;
 
}
 

	
 
/** Update pixel position of the caret. */
 
void Textbuf::UpdateCaretPosition()
 
{
 
	this->caretxoffs = this->chars > 1 ? GetCharPosInString(this->buf, this->buf + this->caretpos, FS_NORMAL).x : 0;
 
}
 

	
 
/**
 
 * Handle text navigation with arrow keys left/right.
 
 * This defines where the caret will blink and the next character interaction will occur
 
@@ -336,12 +344,12 @@ bool Textbuf::MovePos(uint16 keycode)
 

	
 
		case WKC_HOME:
 
			this->caretpos = 0;
 
			this->caretxoffs = 0;
 
			this->UpdateCaretPosition();
 
			return true;
 

	
 
		case WKC_END:
 
			this->caretpos = this->bytes - 1;
 
			this->caretxoffs = this->pixels;
 
			this->UpdateCaretPosition();
 
			return true;
 

	
 
		default:
 
@@ -418,21 +426,20 @@ void Textbuf::UpdateSize()
 
{
 
	const char *buf = this->buf;
 

	
 
	this->pixels = 0;
 
	this->chars = this->bytes = 1; // terminating zero
 

	
 
	WChar c;
 
	while ((c = Utf8Consume(&buf)) != '\0') {
 
		this->pixels += GetCharacterWidth(FS_NORMAL, c);
 
		this->bytes += Utf8CharLen(c);
 
		this->chars++;
 
	}
 

	
 
	assert(this->bytes <= this->max_bytes);
 
	assert(this->chars <= this->max_chars);
 

	
 
	this->caretpos = this->bytes - 1;
 
	this->caretxoffs = this->pixels;
 
	this->UpdateWidth();
 

	
 
	this->UpdateCaretPosition();
 
}
 

	
 
/**
src/textbuf_type.h
Show inline comments
 
@@ -69,6 +69,8 @@ private:
 
	bool CanMoveCaretRight();
 
	WChar MoveCaretRight();
 

	
 
	void UpdateWidth();
 
	void UpdateCaretPosition();
 
};
 

	
 
#endif /* TEXTBUF_TYPE_H */
0 comments (0 inline, 0 general)