Codechange: Store both left and right glyph positions in a run.

This also allows the end of a run to be known without using an extra position entry.
This commit is contained in:
Peter Nelson 2024-05-22 20:36:18 +01:00 committed by Peter Nelson
parent 80ddcb9d7d
commit 5cd81a980e
10 changed files with 68 additions and 61 deletions

View File

@ -349,10 +349,10 @@ struct IConsoleWindow : Window
{
int delta = std::min<int>(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;
}

View File

@ -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;

View File

@ -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<ptrdiff_t>(str.size()));

View File

@ -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<const GlyphID> GetGlyphs() const = 0;
virtual std::span<const Point> GetPositions() const = 0;
virtual std::span<const Position> GetPositions() const = 0;
virtual int GetLeading() const = 0;
virtual std::span<const int> 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 */

View File

@ -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<GlyphID> glyphs; ///< The glyphs we're drawing.
std::vector<Point> positions; ///< The positions of the glyphs.
std::vector<Position> positions; ///< The positions of the glyphs.
std::vector<int> 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<int>(this->glyphs.size()); }
std::span<const GlyphID> GetGlyphs() const override { return this->glyphs; }
std::span<const Point> GetPositions() const override { return this->positions; }
std::span<const Position> GetPositions() const override { return this->positions; }
int GetLeading() const override { return this->GetFont()->fc->GetHeight(); }
std::span<const int> 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;
}
/**

View File

@ -45,7 +45,7 @@ public:
std::vector<GlyphID> glyphs; ///< The glyphs of the run. Valid after Shape() is called.
std::vector<int> advance; ///< The advance (width) of the glyphs. Valid after Shape() is called.
std::vector<int> glyph_to_char; ///< The mapping from glyphs to characters. Valid after Shape() is called.
std::vector<Point> positions; ///< The positions of the glyphs. Valid after Shape() is called.
std::vector<ParagraphLayouter::Position> 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<GlyphID> glyphs;
std::vector<Point> positions;
std::vector<Position> positions;
std::vector<int> glyph_to_char;
int total_advance;
@ -72,7 +72,7 @@ public:
ICUVisualRun(const ICURun &run, int x);
std::span<const GlyphID> GetGlyphs() const override { return this->glyphs; }
std::span<const Point> GetPositions() const override { return this->positions; }
std::span<const Position> GetPositions() const override { return this->positions; }
std::span<const int> 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;

View File

@ -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 };
}
/**

View File

@ -71,7 +71,7 @@ public:
class CoreTextVisualRun : public ParagraphLayouter::VisualRun {
private:
std::vector<GlyphID> glyphs;
std::vector<Point> positions;
std::vector<Position> positions;
std::vector<int> glyph_to_char;
int total_advance = 0;
@ -82,7 +82,7 @@ public:
CoreTextVisualRun(CoreTextVisualRun &&other) = default;
std::span<const GlyphID> GetGlyphs() const override { return this->glyphs; }
std::span<const Point> GetPositions() const override { return this->positions; }
std::span<const Position> GetPositions() const override { return this->positions; }
std::span<const int> 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);
}
/**

View File

@ -74,7 +74,7 @@ public:
class UniscribeVisualRun : public ParagraphLayouter::VisualRun {
private:
std::vector<GlyphID> glyphs;
std::vector<Point> positions;
std::vector<Position> positions;
std::vector<WORD> char_to_glyph;
int start_pos;
@ -89,7 +89,7 @@ public:
UniscribeVisualRun(UniscribeVisualRun &&other) noexcept;
std::span<const GlyphID> GetGlyphs() const override { return this->glyphs; }
std::span<const Point> GetPositions() const override { return this->positions; }
std::span<const Position> GetPositions() const override { return this->positions; }
std::span<const int> 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

View File

@ -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;
}