summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authormichi_cc <michi_cc@openttd.org>2013-08-05 20:35:23 +0000
committermichi_cc <michi_cc@openttd.org>2013-08-05 20:35:23 +0000
commit33f3cf3a5daeaf9e6e5b5414696f93676249bc41 (patch)
tree9de7c6fec9a311f66a3e598cdb3286cfdd1cadff /src
parent9d7ec75fc0a396b47f91a5f7ec4bea59024fea5f (diff)
downloadopenttd-33f3cf3a5daeaf9e6e5b5414696f93676249bc41.tar.xz
(svn r25651) -Fix: Textbuf caret rendering for complex scripts (e.g. Tamil).
Diffstat (limited to 'src')
-rw-r--r--src/gfx.cpp14
-rw-r--r--src/gfx_func.h1
-rw-r--r--src/gfx_layout.cpp55
-rw-r--r--src/gfx_layout.h3
-rw-r--r--src/textbuf.cpp55
-rw-r--r--src/textbuf_type.h2
6 files changed, 105 insertions, 25 deletions
diff --git a/src/gfx.cpp b/src/gfx.cpp
index 3278f972c..0e72e23d7 100644
--- a/src/gfx.cpp
+++ b/src/gfx.cpp
@@ -708,6 +708,20 @@ Dimension GetStringBoundingBox(StringID strid)
}
/**
+ * 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
diff --git a/src/gfx_func.h b/src/gfx_func.h
index 6ec36ee1b..1226816b4 100644
--- a/src/gfx_func.h
+++ b/src/gfx_func.h
@@ -126,6 +126,7 @@ int GetStringLineCount(StringID str, int maxw);
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);
diff --git a/src/gfx_layout.cpp b/src/gfx_layout.cpp
index b47709374..165edee3b 100644
--- a/src/gfx_layout.cpp
+++ b/src/gfx_layout.cpp
@@ -423,7 +423,7 @@ ParagraphLayout *Layouter::GetParagraphLayout(WChar *buff, WChar *buff_end, Font
* @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)
diff --git a/src/gfx_layout.h b/src/gfx_layout.h
index c252d15eb..27c1e9c08 100644
--- a/src/gfx_layout.h
+++ b/src/gfx_layout.h
@@ -168,6 +168,8 @@ class Layouter : public AutoDeleteSmallVector<ParagraphLayout::Line *, 4> {
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 AutoDeleteSmallVector<ParagraphLayout::Line *, 4> {
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();
diff --git a/src/textbuf.cpp b/src/textbuf.cpp
index e84b14e7b..84aa7157e 100644
--- a/src/textbuf.cpp
+++ b/src/textbuf.cpp
@@ -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();
}
/**
diff --git a/src/textbuf_type.h b/src/textbuf_type.h
index d0b75f818..b9d3d1a34 100644
--- a/src/textbuf_type.h
+++ b/src/textbuf_type.h
@@ -69,6 +69,8 @@ private:
bool CanMoveCaretRight();
WChar MoveCaretRight();
+ void UpdateWidth();
+ void UpdateCaretPosition();
};
#endif /* TEXTBUF_TYPE_H */