diff --git a/src/console_gui.cpp b/src/console_gui.cpp index a95a5e0c80..14f8ac7ec2 100644 --- a/src/console_gui.cpp +++ b/src/console_gui.cpp @@ -349,10 +349,10 @@ struct IConsoleWindow : Window { int delta = std::min(this->width - this->line_offset - _iconsole_cmdline.pixels - ICON_RIGHT_BORDERWIDTH, 0); - Point p1 = GetCharPosInString(_iconsole_cmdline.buf, from, FS_NORMAL); - Point p2 = from != to ? GetCharPosInString(_iconsole_cmdline.buf, to, FS_NORMAL) : p1; + const auto p1 = GetCharPosInString(_iconsole_cmdline.buf, from, FS_NORMAL); + const auto p2 = from != to ? GetCharPosInString(_iconsole_cmdline.buf, to, FS_NORMAL) : p1; - Rect r = {this->line_offset + delta + p1.x, this->height - this->line_height, this->line_offset + delta + p2.x, this->height}; + Rect r = {this->line_offset + delta + p1.left, this->height - this->line_height, this->line_offset + delta + p2.right, this->height}; return r; } diff --git a/src/gfx.cpp b/src/gfx.cpp index 51a053a354..d97e343b23 100644 --- a/src/gfx.cpp +++ b/src/gfx.cpp @@ -605,9 +605,9 @@ static int DrawLayoutLine(const ParagraphLayouter::Line &line, int y, int left, /* Not a valid glyph (empty) */ if (glyph == 0xFFFF) continue; - int begin_x = positions[i].x + left - offset_x; - int end_x = positions[i + 1].x + left - offset_x - 1; - int top = positions[i].y + y; + int begin_x = positions[i].left + left - offset_x; + int end_x = positions[i].right + left - offset_x; + int top = positions[i].top + y; /* Truncated away. */ if (truncation && (begin_x < min_x || end_x > max_x)) continue; diff --git a/src/gfx_layout.cpp b/src/gfx_layout.cpp index fc355d79af..ea33b14acf 100644 --- a/src/gfx_layout.cpp +++ b/src/gfx_layout.cpp @@ -225,7 +225,7 @@ static bool IsConsumedFormattingCode(char32_t ch) * @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(std::string_view::const_iterator ch) const +ParagraphLayouter::Position Layouter::GetCharPosition(std::string_view::const_iterator ch) const { const auto &line = this->front(); @@ -245,8 +245,8 @@ Point Layouter::GetCharPosition(std::string_view::const_iterator ch) const } /* Initial position, returned if character not found. */ - const Point initial_position = {_current_text_dir == TD_LTR ? 0 : line->GetWidth(), 0}; - const Point *position = &initial_position; + const ParagraphLayouter::Position initial_position = Point{_current_text_dir == TD_LTR ? 0 : line->GetWidth(), 0}; + const ParagraphLayouter::Position *position = &initial_position; /* We couldn't find the code point index. */ if (str != ch) return *position; @@ -303,8 +303,8 @@ ptrdiff_t Layouter::GetCharAtPosition(int x, size_t line_index) const /* Not a valid glyph (empty). */ if (glyphs[i] == 0xFFFF) continue; - int begin_x = positions[i].x; - int end_x = positions[i + 1].x; + int begin_x = positions[i].left; + int end_x = positions[i].right + 1; if (IsInsideMM(x, begin_x, end_x)) { /* Found our glyph, now convert to UTF-8 string index. */ @@ -418,7 +418,7 @@ void Layouter::ReduceLineCache() * @param start_fontsize Font size to start the text with. * @return Upper left corner of the glyph associated with the character. */ -Point GetCharPosInString(std::string_view str, const char *ch, FontSize start_fontsize) +ParagraphLayouter::Position GetCharPosInString(std::string_view str, const char *ch, FontSize start_fontsize) { /* Ensure "ch" is inside "str" or at the exact end. */ assert(ch >= str.data() && (ch - str.data()) <= static_cast(str.size())); diff --git a/src/gfx_layout.h b/src/gfx_layout.h index 88aa28deb7..b400596c3e 100644 --- a/src/gfx_layout.h +++ b/src/gfx_layout.h @@ -90,6 +90,19 @@ class ParagraphLayouter { public: virtual ~ParagraphLayouter() = default; + /** Position of a glyph within a VisualRun. */ + class Position { + public: + int16_t left; ///< Left-most position of glyph. + int16_t right; ///< Right-most position of glyph. + int16_t top; ///< Top-most position of glyph. + + constexpr inline Position(int16_t left, int16_t right, int16_t top) : left(left), right(right), top(top) { } + + /** Conversion from a single point to a Position. */ + constexpr inline Position(const Point &pt) : left(pt.x), right(pt.x), top(pt.y) { } + }; + /** Visual run contains data about the bit of text with the same font. */ class VisualRun { public: @@ -97,7 +110,7 @@ public: virtual const Font *GetFont() const = 0; virtual int GetGlyphCount() const = 0; virtual std::span GetGlyphs() const = 0; - virtual std::span GetPositions() const = 0; + virtual std::span GetPositions() const = 0; virtual int GetLeading() const = 0; virtual std::span GetGlyphToCharMap() const = 0; }; @@ -176,7 +189,7 @@ public: Layouter(std::string_view str, int maxw = INT32_MAX, FontSize fontsize = FS_NORMAL); Dimension GetBounds(); - Point GetCharPosition(std::string_view::const_iterator ch) const; + ParagraphLayouter::Position GetCharPosition(std::string_view::const_iterator ch) const; ptrdiff_t GetCharAtPosition(int x, size_t line_index) const; static void Initialize(); @@ -185,7 +198,7 @@ public: static void ReduceLineCache(); }; -Point GetCharPosInString(std::string_view str, const char *ch, FontSize start_fontsize = FS_NORMAL); +ParagraphLayouter::Position GetCharPosInString(std::string_view str, const char *ch, FontSize start_fontsize = FS_NORMAL); ptrdiff_t GetCharAtPosition(std::string_view str, int x, FontSize start_fontsize = FS_NORMAL); #endif /* GFX_LAYOUT_H */ diff --git a/src/gfx_layout_fallback.cpp b/src/gfx_layout_fallback.cpp index a169cb75b6..39e5700051 100644 --- a/src/gfx_layout_fallback.cpp +++ b/src/gfx_layout_fallback.cpp @@ -40,7 +40,7 @@ public: /** Visual run contains data about the bit of text with the same font. */ class FallbackVisualRun : public ParagraphLayouter::VisualRun { std::vector glyphs; ///< The glyphs we're drawing. - std::vector positions; ///< The positions of the glyphs. + std::vector positions; ///< The positions of the glyphs. std::vector glyph_to_char; ///< The char index of the glyphs. Font *font; ///< The font used to layout these. @@ -50,7 +50,7 @@ public: const Font *GetFont() const override { return this->font; } int GetGlyphCount() const override { return static_cast(this->glyphs.size()); } std::span GetGlyphs() const override { return this->glyphs; } - std::span GetPositions() const override { return this->positions; } + std::span GetPositions() const override { return this->positions; } int GetLeading() const override { return this->GetFont()->fc->GetHeight(); } std::span GetGlyphToCharMap() const override { return this->glyph_to_char; } }; @@ -116,25 +116,22 @@ FallbackParagraphLayout::FallbackVisualRun::FallbackVisualRun(Font *font, const this->glyphs.reserve(char_count); this->glyph_to_char.reserve(char_count); - - /* Positions contains the location of the begin of each of the glyphs, and the end of the last one. */ - this->positions.reserve(char_count + 1); + this->positions.reserve(char_count); int advance = x; for (int i = 0; i < char_count; i++) { const GlyphID &glyph_id = this->glyphs.emplace_back(font->fc->MapCharToGlyph(chars[i])); + int x_advance = font->fc->GetGlyphWidth(glyph_id); if (isbuiltin) { - this->positions.emplace_back(advance, font->fc->GetAscender()); // Apply sprite font's ascender. + this->positions.emplace_back(advance, advance + x_advance - 1, font->fc->GetAscender()); // Apply sprite font's ascender. } else if (chars[i] >= SCC_SPRITE_START && chars[i] <= SCC_SPRITE_END) { - this->positions.emplace_back(advance, (font->fc->GetHeight() - ScaleSpriteTrad(FontCache::GetDefaultFontHeight(font->fc->GetSize()))) / 2); // Align sprite font to centre + this->positions.emplace_back(advance, advance + x_advance - 1, (font->fc->GetHeight() - ScaleSpriteTrad(FontCache::GetDefaultFontHeight(font->fc->GetSize()))) / 2); // Align sprite font to centre } else { - this->positions.emplace_back(advance, 0); // No ascender adjustment. + this->positions.emplace_back(advance, advance + x_advance - 1, 0); // No ascender adjustment. } - advance += font->fc->GetGlyphWidth(glyph_id); + advance += x_advance; this->glyph_to_char.push_back(char_offset + i); } - /* End-of-run position. */ - this->positions.emplace_back(advance, 0); } /** @@ -165,7 +162,9 @@ int FallbackParagraphLayout::FallbackLine::GetWidth() const * the last run gives us the end of the line and thus the width. */ const auto &run = this->GetVisualRun(this->CountRuns() - 1); - return run.GetPositions().back().x; + const auto &positions = run.GetPositions(); + if (positions.empty()) return 0; + return positions.back().right + 1; } /** diff --git a/src/gfx_layout_icu.cpp b/src/gfx_layout_icu.cpp index 9947e62391..6a5dcbe1e7 100644 --- a/src/gfx_layout_icu.cpp +++ b/src/gfx_layout_icu.cpp @@ -45,7 +45,7 @@ public: std::vector glyphs; ///< The glyphs of the run. Valid after Shape() is called. std::vector advance; ///< The advance (width) of the glyphs. Valid after Shape() is called. std::vector glyph_to_char; ///< The mapping from glyphs to characters. Valid after Shape() is called. - std::vector positions; ///< The positions of the glyphs. Valid after Shape() is called. + std::vector positions; ///< The positions of the glyphs. Valid after Shape() is called. int total_advance = 0; ///< The total advance of the run. Valid after Shape() is called. ICURun(int start, int length, UBiDiLevel level, UScriptCode script = USCRIPT_UNKNOWN, Font *font = nullptr) : start(start), length(length), level(level), script(script), font(font) {} @@ -62,7 +62,7 @@ public: class ICUVisualRun : public ParagraphLayouter::VisualRun { private: std::vector glyphs; - std::vector positions; + std::vector positions; std::vector glyph_to_char; int total_advance; @@ -72,7 +72,7 @@ public: ICUVisualRun(const ICURun &run, int x); std::span GetGlyphs() const override { return this->glyphs; } - std::span GetPositions() const override { return this->positions; } + std::span GetPositions() const override { return this->positions; } std::span GetGlyphToCharMap() const override { return this->glyph_to_char; } const Font *GetFont() const override { return this->font; } @@ -136,8 +136,8 @@ ICUParagraphLayout::ICUVisualRun::ICUVisualRun(const ICURun &run, int x) : this->positions.reserve(run.positions.size()); /* Copy positions, moving x coordinate by x offset. */ - for (const Point &pt : run.positions) { - this->positions.emplace_back(pt.x + x, pt.y); + for (const auto &pos : run.positions) { + this->positions.emplace_back(pos.left + x, pos.right + x, pos.top); } } @@ -179,7 +179,7 @@ void ICURun::Shape(UChar *buff, size_t buff_length) /* Reserve space, as we already know the size. */ this->glyphs.reserve(glyph_count); this->glyph_to_char.reserve(glyph_count); - this->positions.reserve(glyph_count + 1); + this->positions.reserve(glyph_count); this->advance.reserve(glyph_count); /* Prepare the glyphs/position. ICUVisualRun will give the position an offset if needed. */ @@ -189,14 +189,13 @@ void ICURun::Shape(UChar *buff, size_t buff_length) if (buff[glyph_info[i].cluster] >= SCC_SPRITE_START && buff[glyph_info[i].cluster] <= SCC_SPRITE_END && glyph_info[i].codepoint == 0) { auto glyph = this->font->fc->MapCharToGlyph(buff[glyph_info[i].cluster]); - - this->glyphs.push_back(glyph); - this->positions.emplace_back(advance, (this->font->fc->GetHeight() - ScaleSpriteTrad(FontCache::GetDefaultFontHeight(this->font->fc->GetSize()))) / 2); // Align sprite font to centre x_advance = this->font->fc->GetGlyphWidth(glyph); + this->glyphs.push_back(glyph); + this->positions.emplace_back(advance, advance + x_advance - 1, (this->font->fc->GetHeight() - ScaleSpriteTrad(FontCache::GetDefaultFontHeight(this->font->fc->GetSize()))) / 2); // Align sprite font to centre } else { - this->glyphs.push_back(glyph_info[i].codepoint); - this->positions.emplace_back(glyph_pos[i].x_offset / FONT_SCALE + advance, glyph_pos[i].y_offset / FONT_SCALE); x_advance = glyph_pos[i].x_advance / FONT_SCALE; + this->glyphs.push_back(glyph_info[i].codepoint); + this->positions.emplace_back(glyph_pos[i].x_offset / FONT_SCALE + advance, glyph_pos[i].x_offset / FONT_SCALE + advance + x_advance - 1, glyph_pos[i].y_offset / FONT_SCALE); } this->glyph_to_char.push_back(glyph_info[i].cluster); @@ -204,9 +203,6 @@ void ICURun::Shape(UChar *buff, size_t buff_length) advance += x_advance; } - /* End-of-run position. */ - this->positions.emplace_back(advance, 0); - /* Track the total advancement we made. */ this->total_advance = advance; diff --git a/src/misc_gui.cpp b/src/misc_gui.cpp index ea5e01cb86..f0de0d1cf4 100644 --- a/src/misc_gui.cpp +++ b/src/misc_gui.cpp @@ -902,10 +902,10 @@ Rect QueryString::GetBoundingRect(const Window *w, WidgetID wid, const char *fro r = ScrollEditBoxTextRect(r, *tb); /* Get location of first and last character. */ - Point p1 = GetCharPosInString(tb->buf, from, FS_NORMAL); - Point p2 = from != to ? GetCharPosInString(tb->buf, to, FS_NORMAL) : p1; + const auto p1 = GetCharPosInString(tb->buf, from, FS_NORMAL); + const auto p2 = from != to ? GetCharPosInString(tb->buf, to, FS_NORMAL) : p1; - return { Clamp(r.left + p1.x, r.left, r.right), r.top, Clamp(r.left + p2.x, r.left, r.right), r.bottom }; + return { Clamp(r.left + p1.left, r.left, r.right), r.top, Clamp(r.left + p2.right, r.left, r.right), r.bottom }; } /** diff --git a/src/os/macosx/string_osx.cpp b/src/os/macosx/string_osx.cpp index 08cf974f23..cac25b46df 100644 --- a/src/os/macosx/string_osx.cpp +++ b/src/os/macosx/string_osx.cpp @@ -71,7 +71,7 @@ public: class CoreTextVisualRun : public ParagraphLayouter::VisualRun { private: std::vector glyphs; - std::vector positions; + std::vector positions; std::vector glyph_to_char; int total_advance = 0; @@ -82,7 +82,7 @@ public: CoreTextVisualRun(CoreTextVisualRun &&other) = default; std::span GetGlyphs() const override { return this->glyphs; } - std::span GetPositions() const override { return this->positions; } + std::span GetPositions() const override { return this->positions; } std::span GetGlyphToCharMap() const override { return this->glyph_to_char; } const Font *GetFont() const override { return this->font; } @@ -241,7 +241,9 @@ CoreTextParagraphLayout::CoreTextVisualRun::CoreTextVisualRun(CTRunRef run, Font CGPoint pts[this->glyphs.size()]; CTRunGetPositions(run, CFRangeMake(0, 0), pts); - this->positions.reserve(this->glyphs.size() + 1); + CGSize advs[this->glyphs.size()]; + CTRunGetAdvances(run, CFRangeMake(0, 0), advs); + this->positions.reserve(this->glyphs.size()); /* Convert glyph array to our data type. At the same time, substitute * the proper glyphs for our private sprite glyphs. */ @@ -251,15 +253,13 @@ CoreTextParagraphLayout::CoreTextVisualRun::CoreTextVisualRun(CTRunRef run, Font if (buff[this->glyph_to_char[i]] >= SCC_SPRITE_START && buff[this->glyph_to_char[i]] <= SCC_SPRITE_END && (gl[i] == 0 || gl[i] == 3)) { /* A glyph of 0 indidicates not found, while apparently 3 is what char 0xFFFC maps to. */ this->glyphs[i] = font->fc->MapCharToGlyph(buff[this->glyph_to_char[i]]); - this->positions.emplace_back(pts[i].x, (font->fc->GetHeight() - ScaleSpriteTrad(FontCache::GetDefaultFontHeight(font->fc->GetSize()))) / 2); // Align sprite font to centre + this->positions.emplace_back(pts[i].x, pts[i].x + advs[i].width - 1, (font->fc->GetHeight() - ScaleSpriteTrad(FontCache::GetDefaultFontHeight(font->fc->GetSize()))) / 2); // Align sprite font to centre } else { this->glyphs[i] = gl[i]; - this->positions.emplace_back(pts[i].x, pts[i].y); + this->positions.emplace_back(pts[i].x, pts[i].x + advs[i].width - 1, pts[i].y); } } this->total_advance = (int)std::ceil(CTRunGetTypographicBounds(run, CFRangeMake(0, 0), nullptr, nullptr, nullptr)); - /* End-of-run position. */ - this->positions.emplace_back(this->positions.front().x + this->total_advance, 0); } /** diff --git a/src/os/windows/string_uniscribe.cpp b/src/os/windows/string_uniscribe.cpp index 7b289734d2..0834fe6e56 100644 --- a/src/os/windows/string_uniscribe.cpp +++ b/src/os/windows/string_uniscribe.cpp @@ -74,7 +74,7 @@ public: class UniscribeVisualRun : public ParagraphLayouter::VisualRun { private: std::vector glyphs; - std::vector positions; + std::vector positions; std::vector char_to_glyph; int start_pos; @@ -89,7 +89,7 @@ public: UniscribeVisualRun(UniscribeVisualRun &&other) noexcept; std::span GetGlyphs() const override { return this->glyphs; } - std::span GetPositions() const override { return this->positions; } + std::span GetPositions() const override { return this->positions; } std::span GetGlyphToCharMap() const override; const Font *GetFont() const override { return this->font; } @@ -474,16 +474,15 @@ int UniscribeParagraphLayout::UniscribeLine::GetWidth() const UniscribeParagraphLayout::UniscribeVisualRun::UniscribeVisualRun(const UniscribeRun &range, int x) : glyphs(range.ft_glyphs), char_to_glyph(range.char_to_glyph), start_pos(range.pos), total_advance(range.total_advance), font(range.font) { this->num_glyphs = (int)glyphs.size(); - this->positions.reserve(this->num_glyphs + 1); + this->positions.reserve(this->num_glyphs); int advance = x; for (int i = 0; i < this->num_glyphs; i++) { - this->positions.emplace_back(range.offsets[i].du + advance, range.offsets[i].dv); + int x_advance = range.advances[i]; + this->positions.emplace_back(range.offsets[i].du + advance - 1, range.offsets[i].du + advance + x_advance, range.offsets[i].dv); - advance += range.advances[i]; + advance += x_advance; } - /* End-of-run position. */ - this->positions.emplace_back(advance, 0); } UniscribeParagraphLayout::UniscribeVisualRun::UniscribeVisualRun(UniscribeVisualRun&& other) noexcept diff --git a/src/textbuf.cpp b/src/textbuf.cpp index ee09223a75..4da9365791 100644 --- a/src/textbuf.cpp +++ b/src/textbuf.cpp @@ -309,15 +309,15 @@ void Textbuf::UpdateWidth() /** 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; + this->caretxoffs = this->chars > 1 ? GetCharPosInString(this->buf, this->buf + this->caretpos, FS_NORMAL).left : 0; } /** Update pixel positions of the marked text area. */ void Textbuf::UpdateMarkedText() { if (this->markend != 0) { - this->markxoffs = GetCharPosInString(this->buf, this->buf + this->markpos, FS_NORMAL).x; - this->marklength = GetCharPosInString(this->buf, this->buf + this->markend, FS_NORMAL).x - this->markxoffs; + this->markxoffs = GetCharPosInString(this->buf, this->buf + this->markpos, FS_NORMAL).left; + this->marklength = GetCharPosInString(this->buf, this->buf + this->markend, FS_NORMAL).left - this->markxoffs; } else { this->markxoffs = this->marklength = 0; }