mirror of
https://github.com/daveallie/crosspoint-reader.git
synced 2026-02-06 15:47:39 +03:00
perf: Add binary search for TXT word wrap
Use binary search instead of linear search for finding line break positions. This significantly improves TXT file indexing performance, especially with SD card fonts where getTextWidth() calls are slower.
This commit is contained in:
parent
00134bbe80
commit
fa4c63e54b
@ -19,6 +19,70 @@ constexpr size_t CHUNK_SIZE = 8 * 1024; // 8KB chunk for reading
|
||||
// Cache file magic and version
|
||||
constexpr uint32_t CACHE_MAGIC = 0x54585449; // "TXTI"
|
||||
constexpr uint8_t CACHE_VERSION = 2; // Increment when cache format changes
|
||||
|
||||
// Find UTF-8 character boundary at or before pos
|
||||
size_t findUtf8Boundary(const std::string& str, size_t pos) {
|
||||
if (pos >= str.length()) return str.length();
|
||||
// Move back if we're in the middle of a UTF-8 sequence
|
||||
while (pos > 0 && (str[pos] & 0xC0) == 0x80) {
|
||||
pos--;
|
||||
}
|
||||
return pos;
|
||||
}
|
||||
|
||||
// Binary search to find max characters that fit in width
|
||||
// Returns the position (byte offset) where to break the string
|
||||
size_t findBreakPosition(GfxRenderer& renderer, int fontId, const std::string& line, int maxWidth) {
|
||||
if (line.empty()) return 0;
|
||||
|
||||
// First check if the whole line fits
|
||||
int fullWidth = renderer.getTextWidth(fontId, line.c_str());
|
||||
if (fullWidth <= maxWidth) {
|
||||
return line.length();
|
||||
}
|
||||
|
||||
// Binary search for the break point
|
||||
size_t low = 1; // At minimum 1 character
|
||||
size_t high = line.length();
|
||||
size_t bestFit = 1;
|
||||
|
||||
while (low < high) {
|
||||
size_t mid = (low + high + 1) / 2;
|
||||
mid = findUtf8Boundary(line, mid);
|
||||
|
||||
if (mid <= low) {
|
||||
// Can't make progress, exit
|
||||
break;
|
||||
}
|
||||
|
||||
std::string substr = line.substr(0, mid);
|
||||
int width = renderer.getTextWidth(fontId, substr.c_str());
|
||||
|
||||
if (width <= maxWidth) {
|
||||
bestFit = mid;
|
||||
low = mid;
|
||||
} else {
|
||||
high = mid - 1;
|
||||
if (high > 0) {
|
||||
high = findUtf8Boundary(line, high);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Try to break at word boundary (space) if possible
|
||||
if (bestFit > 0 && bestFit < line.length()) {
|
||||
size_t spacePos = line.rfind(' ', bestFit);
|
||||
if (spacePos != std::string::npos && spacePos > 0) {
|
||||
// Check if breaking at space still fits
|
||||
std::string atSpace = line.substr(0, spacePos);
|
||||
if (renderer.getTextWidth(fontId, atSpace.c_str()) <= maxWidth) {
|
||||
return spacePos;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return bestFit > 0 ? bestFit : 1; // At minimum, consume 1 character
|
||||
}
|
||||
} // namespace
|
||||
|
||||
void TxtReaderActivity::taskTrampoline(void* param) {
|
||||
@ -303,36 +367,21 @@ bool TxtReaderActivity::loadPageAtOffset(size_t offset, std::vector<std::string>
|
||||
// Track position within this source line (in bytes from pos)
|
||||
size_t lineBytePos = 0;
|
||||
|
||||
// Word wrap if needed
|
||||
// Word wrap if needed - use binary search for performance with SD fonts
|
||||
while (!line.empty() && static_cast<int>(outLines.size()) < linesPerPage) {
|
||||
int lineWidth = renderer.getTextWidth(cachedFontId, line.c_str());
|
||||
// Use binary search to find break position (much faster than linear search)
|
||||
size_t breakPos = findBreakPosition(renderer, cachedFontId, line, viewportWidth);
|
||||
|
||||
if (lineWidth <= viewportWidth) {
|
||||
if (breakPos >= line.length()) {
|
||||
// Whole line fits
|
||||
outLines.push_back(line);
|
||||
lineBytePos = displayLen; // Consumed entire display content
|
||||
lineBytePos = displayLen;
|
||||
line.clear();
|
||||
break;
|
||||
}
|
||||
|
||||
// Find break point
|
||||
size_t breakPos = line.length();
|
||||
while (breakPos > 0 && renderer.getTextWidth(cachedFontId, line.substr(0, breakPos).c_str()) > viewportWidth) {
|
||||
// Try to break at space
|
||||
size_t spacePos = line.rfind(' ', breakPos - 1);
|
||||
if (spacePos != std::string::npos && spacePos > 0) {
|
||||
breakPos = spacePos;
|
||||
} else {
|
||||
// Break at character boundary for UTF-8
|
||||
breakPos--;
|
||||
// Make sure we don't break in the middle of a UTF-8 sequence
|
||||
while (breakPos > 0 && (line[breakPos] & 0xC0) == 0x80) {
|
||||
breakPos--;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (breakPos == 0) {
|
||||
breakPos = 1;
|
||||
breakPos = 1; // Ensure progress
|
||||
}
|
||||
|
||||
outLines.push_back(line.substr(0, breakPos));
|
||||
|
||||
Loading…
Reference in New Issue
Block a user