Optimize Fl_Text_Display scrolling speed (#596).
This commit is contained in:
parent
bc73580366
commit
9bb9cb3f96
@ -635,6 +635,13 @@ public:
|
||||
*/
|
||||
int count_lines(int startPos, int endPos) const;
|
||||
|
||||
/**
|
||||
Estimate the number of newlines between \p startPos and \p endPos in buffer.
|
||||
This call takes line wrapping into account. It assumes a line break at every
|
||||
`lineLen` characters after the beginning of a line.
|
||||
*/
|
||||
int estimate_lines(int startPos, int endPos, int lineLen) const;
|
||||
|
||||
/**
|
||||
Finds the first character of the line \p nLines forward from \p startPos
|
||||
in the buffer and returns its position.
|
||||
|
@ -1166,6 +1166,48 @@ int Fl_Text_Buffer::count_lines(int startPos, int endPos) const {
|
||||
return lineCount;
|
||||
}
|
||||
|
||||
/**
|
||||
Estimate the number of newlines between \p startPos and \p endPos in buffer.
|
||||
This call takes line wrapping into account. It assumes a line break at every
|
||||
`lineLen` characters after the beginning of a line.
|
||||
*/
|
||||
int Fl_Text_Buffer::estimate_lines(int startPos, int endPos, int lineLen) const
|
||||
{
|
||||
IS_UTF8_ALIGNED2(this, (startPos))
|
||||
IS_UTF8_ALIGNED2(this, (endPos))
|
||||
|
||||
int gapLen = mGapEnd - mGapStart;
|
||||
int lineCount = 0;
|
||||
int softLineBreaks = 0, softLineBreakCount = lineLen;
|
||||
|
||||
int pos = startPos;
|
||||
while (pos < mGapStart)
|
||||
{
|
||||
if (pos == endPos)
|
||||
return lineCount + softLineBreaks;
|
||||
if (mBuf[pos++] == '\n') {
|
||||
softLineBreakCount = lineLen;
|
||||
lineCount++;
|
||||
}
|
||||
if (--softLineBreakCount == 0) {
|
||||
softLineBreakCount = lineLen;
|
||||
softLineBreaks++;
|
||||
}
|
||||
}
|
||||
while (pos < mLength) {
|
||||
if (pos == endPos)
|
||||
return lineCount + softLineBreaks;
|
||||
if (mBuf[pos++ + gapLen] == '\n') {
|
||||
softLineBreakCount = lineLen;
|
||||
lineCount++;
|
||||
}
|
||||
if (--softLineBreakCount == 0) {
|
||||
softLineBreakCount = lineLen;
|
||||
softLineBreaks++;
|
||||
}
|
||||
}
|
||||
return lineCount + softLineBreaks;
|
||||
}
|
||||
|
||||
/*
|
||||
Skip to the first character, n lines ahead.
|
||||
|
@ -560,9 +560,9 @@ void Fl_Text_Display::recalc_display() {
|
||||
if (mContinuousWrap && !mWrapMarginPix && text_area.w != oldTAWidth) {
|
||||
|
||||
int oldFirstChar = mFirstChar;
|
||||
mNBufferLines = count_lines(0, buffer()->length(), true);
|
||||
mFirstChar = line_start(mFirstChar);
|
||||
mTopLineNum = count_lines(0, mFirstChar, true)+1;
|
||||
mNBufferLines = mTopLineNum-1 + count_lines(mFirstChar, buffer()->length(), true);
|
||||
absolute_top_line_number(oldFirstChar);
|
||||
#ifdef DEBUG2
|
||||
printf(" mNBufferLines=%d\n", mNBufferLines);
|
||||
@ -1436,6 +1436,56 @@ int Fl_Text_Display::count_lines(int startPos, int endPos,
|
||||
if (!mContinuousWrap)
|
||||
return buffer()->count_lines(startPos, endPos);
|
||||
|
||||
/*
|
||||
Correctly counting wrapped lines is very slow. We have to query the length
|
||||
of every segment of text for every line change and style change and find
|
||||
potential soft line breaks.
|
||||
|
||||
Most of the resulting information is needed for calculating the vertical
|
||||
scroll bar size. After a certain text length, the scroll bar size is no
|
||||
longer very precise anyway, so we optimize line count for all lines but
|
||||
the visible ones (plus minus a few lines for rounding).
|
||||
|
||||
The optimized code is several magnitudes faster and makes scrolling and
|
||||
window resizing of long texts quite responsive. There is a slight but IMHO
|
||||
tollerable drawback: when walking huge files using arrow up and down, the
|
||||
text display sometimes jumps 2 or 3 lines instead of 1, but the overall
|
||||
buffer stays intact as well as the scroll position.
|
||||
*/
|
||||
if (buffer()->length() > 16384) {
|
||||
// Optimized line counting
|
||||
int nLines = 0;
|
||||
int firstVisibleChar = buffer()->rewind_lines(mFirstChar, 3);
|
||||
int lastVisibleChar = buffer()->skip_lines(mLastChar, 3);
|
||||
// Calculate the averga number of characters up to a soft line break
|
||||
if (mColumnScale==0.0) x_to_col(1.0);
|
||||
int avgCharsPerLine = mWrapMarginPix;
|
||||
if (!avgCharsPerLine) avgCharsPerLine = text_area.w;
|
||||
avgCharsPerLine = (int)(avgCharsPerLine / mColumnScale) + 1;
|
||||
|
||||
// first segment, lines up to display, count fast
|
||||
if (startPos < firstVisibleChar) {
|
||||
int tmpEnd = endPos<firstVisibleChar ? endPos : firstVisibleChar;
|
||||
nLines += buffer()->estimate_lines(startPos, tmpEnd, avgCharsPerLine);
|
||||
startPos = tmpEnd;
|
||||
}
|
||||
// second segement, count displayed liens
|
||||
if (startPos < endPos && startPos < mLastChar) {
|
||||
// Precisse line counting only for visible text:
|
||||
int tmpEnd = endPos<lastVisibleChar ? endPos : lastVisibleChar;
|
||||
wrapped_line_counter(buffer(), startPos, tmpEnd, INT_MAX,
|
||||
startPosIsLineStart, 0, &retPos, &retLines, &retLineStart,
|
||||
&retLineEnd);
|
||||
nLines += retLines;
|
||||
startPos = tmpEnd;
|
||||
}
|
||||
// third segement is everything after displayed lines
|
||||
if (startPos < endPos && startPos >= lastVisibleChar) {
|
||||
nLines += buffer()->estimate_lines(startPos, endPos, avgCharsPerLine);
|
||||
}
|
||||
return nLines;
|
||||
} else {
|
||||
// Precise line counting only for small text buffer sizes:
|
||||
wrapped_line_counter(buffer(), startPos, endPos, INT_MAX,
|
||||
startPosIsLineStart, 0, &retPos, &retLines, &retLineStart,
|
||||
&retLineEnd);
|
||||
@ -1444,8 +1494,8 @@ int Fl_Text_Display::count_lines(int startPos, int endPos,
|
||||
printf(" # after WLC: retPos=%d, retLines=%d, retLineStart=%d, retLineEnd=%d\n",
|
||||
retPos, retLines, retLineStart, retLineEnd);
|
||||
#endif // DEBUG
|
||||
|
||||
return retLines;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user