mirror of
https://github.com/telegramdesktop/tdesktop
synced 2025-01-10 00:29:31 +00:00
fixed gif frame delays, no-repaint-on-scroll optimization done for history
This commit is contained in:
parent
5a87bf4114
commit
85bb526294
@ -225,7 +225,7 @@ ClipReader::ClipReader(const FileLocation &location, const QByteArray &data, Cal
|
||||
, _state(ClipReading)
|
||||
, _width(0)
|
||||
, _height(0)
|
||||
, _step(FirstFrameNotReadStep)
|
||||
, _step(WaitingForDimensionsStep)
|
||||
, _paused(0)
|
||||
, _autoplay(false)
|
||||
, _private(0) {
|
||||
@ -248,47 +248,69 @@ ClipReader::ClipReader(const FileLocation &location, const QByteArray &data, Cal
|
||||
_clipManagers.at(_threadIndex)->append(this, location, data);
|
||||
}
|
||||
|
||||
ClipReader::Frame *ClipReader::frameToShow() const { // 0 means not ready
|
||||
int32 step = _step.loadAcquire();
|
||||
if (step == FirstFrameNotReadStep) {
|
||||
return 0;
|
||||
} else if (step == WaitingForRequestStep) {
|
||||
return _frames;
|
||||
}
|
||||
return _frames + (((step + 1) / 2) % 3);
|
||||
}
|
||||
|
||||
ClipReader::Frame *ClipReader::frameToWrite(int32 *index) const { // 0 means not ready
|
||||
int32 step = _step.loadAcquire(), i = 0;
|
||||
if (step == WaitingForRequestStep) {
|
||||
ClipReader::Frame *ClipReader::frameToShow(int32 *index) const { // 0 means not ready
|
||||
int32 step = _step.loadAcquire(), i;
|
||||
if (step == WaitingForDimensionsStep) {
|
||||
if (index) *index = 0;
|
||||
return 0;
|
||||
} else if (step != FirstFrameNotReadStep) {
|
||||
i = (((step + 3) / 2) % 3);
|
||||
} else if (step == WaitingForRequestStep) {
|
||||
i = 0;
|
||||
} else if (step == WaitingForFirstFrameStep) {
|
||||
i = 0;
|
||||
} else {
|
||||
i = (step / 2) % 3;
|
||||
}
|
||||
if (index) *index = i;
|
||||
return _frames + i;
|
||||
}
|
||||
|
||||
ClipReader::Frame *ClipReader::frameToWriteNext(bool checkNotWriting) const {
|
||||
int32 step = _step.loadAcquire();
|
||||
if (step == FirstFrameNotReadStep || step == WaitingForRequestStep || (checkNotWriting && !(step % 2))) {
|
||||
ClipReader::Frame *ClipReader::frameToWrite(int32 *index) const { // 0 means not ready
|
||||
int32 step = _step.loadAcquire(), i;
|
||||
if (step == WaitingForDimensionsStep) {
|
||||
i = 0;
|
||||
} else if (step == WaitingForRequestStep) {
|
||||
if (index) *index = 0;
|
||||
return 0;
|
||||
} else if (step == WaitingForFirstFrameStep) {
|
||||
i = 0;
|
||||
} else {
|
||||
i = ((step + 2) / 2) % 3;
|
||||
}
|
||||
if (index) *index = i;
|
||||
return _frames + i;
|
||||
}
|
||||
|
||||
ClipReader::Frame *ClipReader::frameToWriteNext(bool checkNotWriting, int32 *index) const {
|
||||
int32 step = _step.loadAcquire(), i;
|
||||
if (step == WaitingForDimensionsStep || step == WaitingForRequestStep || (checkNotWriting && (step % 2))) {
|
||||
if (index) *index = 0;
|
||||
return 0;
|
||||
}
|
||||
return _frames + (((step + 5) / 2) % 3);
|
||||
i = ((step + 4) / 2) % 3;
|
||||
if (index) *index = i;
|
||||
return _frames + i;
|
||||
}
|
||||
|
||||
void ClipReader::moveToNextShow() const {
|
||||
int32 step = _step.loadAcquire();
|
||||
if (step % 2) {
|
||||
_step.storeRelease((step + 1) % 6);
|
||||
if (step == WaitingForDimensionsStep) {
|
||||
} else if (step == WaitingForRequestStep) {
|
||||
_step.storeRelease(WaitingForFirstFrameStep);
|
||||
} else if (step == WaitingForFirstFrameStep) {
|
||||
} else if (!(step % 2)) {
|
||||
_step.storeRelease(step + 1);
|
||||
}
|
||||
}
|
||||
|
||||
void ClipReader::moveToNextWrite() const {
|
||||
int32 step = _step.loadAcquire();
|
||||
if (!(step % 2)) {
|
||||
_step.storeRelease(step + 1);
|
||||
if (step == WaitingForDimensionsStep) {
|
||||
_step.storeRelease(WaitingForRequestStep);
|
||||
} else if (step == WaitingForRequestStep) {
|
||||
} else if (step == WaitingForFirstFrameStep) {
|
||||
_step.storeRelease(0);
|
||||
} else if (step % 2) {
|
||||
_step.storeRelease((step + 1) % 6);
|
||||
}
|
||||
}
|
||||
|
||||
@ -313,7 +335,7 @@ void ClipReader::start(int32 framew, int32 frameh, int32 outerw, int32 outerh, b
|
||||
request.outerh = outerh * factor;
|
||||
request.rounded = rounded;
|
||||
_frames[0].request = _frames[1].request = _frames[2].request = request;
|
||||
_step.storeRelease(0); // start working
|
||||
moveToNextShow();
|
||||
_clipManagers.at(_threadIndex)->start(this);
|
||||
}
|
||||
}
|
||||
@ -322,9 +344,8 @@ QPixmap ClipReader::current(int32 framew, int32 frameh, int32 outerw, int32 oute
|
||||
Frame *frame = frameToShow();
|
||||
t_assert(frame != 0);
|
||||
|
||||
frame->displayed = true;
|
||||
if (ms) {
|
||||
frame->when = ms;
|
||||
frame->displayed.storeRelease(1);
|
||||
if (_paused.loadAcquire()) {
|
||||
_paused.storeRelease(0);
|
||||
if (_clipManagers.size() <= _threadIndex) error();
|
||||
@ -332,6 +353,8 @@ QPixmap ClipReader::current(int32 framew, int32 frameh, int32 outerw, int32 oute
|
||||
_clipManagers.at(_threadIndex)->update(this);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
frame->displayed.storeRelease(-1); // displayed, but should be paused
|
||||
}
|
||||
|
||||
int32 factor(cIntRetinaFactor());
|
||||
@ -835,9 +858,7 @@ public:
|
||||
, _frame(0)
|
||||
, _width(0)
|
||||
, _height(0)
|
||||
, _previousMs(0)
|
||||
, _currentMs(0)
|
||||
, _nextUpdateMs(0)
|
||||
, _nextFrameWhen(0)
|
||||
, _paused(false) {
|
||||
if (_data.isEmpty() && !_location->accessEnable()) {
|
||||
error();
|
||||
@ -868,23 +889,19 @@ public:
|
||||
return start(ms);
|
||||
}
|
||||
|
||||
if (!_paused && ms >= _nextUpdateMs) {
|
||||
if (!_paused && ms >= _nextFrameWhen) {
|
||||
return ClipProcessRepaint;
|
||||
}
|
||||
return ClipProcessWait;
|
||||
}
|
||||
|
||||
ClipProcessResult finishProcess(uint64 ms) {
|
||||
_previousMs = _currentMs;
|
||||
if (!prepareNextFrame()) {
|
||||
return error();
|
||||
}
|
||||
if (ms >= _nextUpdateMs) {
|
||||
if (!prepareNextFrame()) {
|
||||
return error();
|
||||
}
|
||||
if (ms >= _nextFrameWhen && !prepareNextFrame(true)) {
|
||||
return error();
|
||||
}
|
||||
_currentMs = qMax(ms, _nextUpdateMs);
|
||||
return ClipProcessCopyFrame;
|
||||
}
|
||||
|
||||
@ -893,15 +910,17 @@ public:
|
||||
return qMax(delay, 5);
|
||||
}
|
||||
|
||||
bool prepareNextFrame() {
|
||||
bool prepareNextFrame(bool keepup = false) {
|
||||
t_assert(frame() != 0 && _request.valid());
|
||||
if (!_implementation->readNextFrame(frame()->original, frame()->alpha, QSize(_request.framew, _request.frameh))) {
|
||||
return false;
|
||||
}
|
||||
_nextUpdateMs = _currentMs + nextFrameDelay();
|
||||
_nextFrameWhen += nextFrameDelay();
|
||||
if (keepup) _nextFrameWhen = qMax(_nextFrameWhen, getms());
|
||||
frame()->original.setDevicePixelRatio(_request.factor);
|
||||
frame()->pix = QPixmap();
|
||||
frame()->pix = _prepareFrame(_request, frame()->original, frame()->alpha, frame()->cache);
|
||||
frame()->when = _nextFrameWhen;
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -962,11 +981,12 @@ private:
|
||||
|
||||
ClipFrameRequest _request;
|
||||
struct Frame {
|
||||
Frame() : alpha(true) {
|
||||
Frame() : alpha(true), when(0) {
|
||||
}
|
||||
QPixmap pix;
|
||||
QImage original, cache;
|
||||
bool alpha;
|
||||
uint64 when;
|
||||
};
|
||||
Frame _frames[3];
|
||||
int32 _frame;
|
||||
@ -976,7 +996,7 @@ private:
|
||||
|
||||
int32 _width, _height;
|
||||
|
||||
uint64 _previousMs, _currentMs, _nextUpdateMs;
|
||||
uint64 _nextFrameWhen;
|
||||
|
||||
bool _paused;
|
||||
|
||||
@ -1070,13 +1090,16 @@ bool ClipReadManager::handleProcessResult(ClipReaderPrivate *reader, ClipProcess
|
||||
if (result == ClipProcessStarted) {
|
||||
_loadLevel.fetchAndAddRelaxed(reader->_width * reader->_height - AverageGifSize);
|
||||
}
|
||||
if (!reader->_paused && (result == ClipProcessRepaint || result == ClipProcessWait)) {
|
||||
ClipReader::Frame *other = it.key()->frameToWriteNext(false);
|
||||
t_assert(other != 0);
|
||||
if (other->when && other->when + WaitBeforeGifPause < qMax(reader->_previousMs, ms)) {
|
||||
reader->_paused = true;
|
||||
it.key()->_paused.storeRelease(1);
|
||||
result = ClipProcessPaused;
|
||||
if (!reader->_paused && result == ClipProcessRepaint) {
|
||||
int32 ishowing, iprevious;
|
||||
ClipReader::Frame *showing = it.key()->frameToShow(&ishowing), *previous = it.key()->frameToWriteNext(false, &iprevious);
|
||||
t_assert(previous != 0 && showing != 0 && ishowing >= 0 && iprevious >= 0);
|
||||
if (reader->_frames[ishowing].when > 0 && showing->displayed.loadAcquire() <= 0) { // current frame was not shown
|
||||
if (reader->_frames[ishowing].when + WaitBeforeGifPause < ms || (reader->_frames[iprevious].when && previous->displayed.loadAcquire() <= 0)) {
|
||||
reader->_paused = true;
|
||||
it.key()->_paused.storeRelease(1);
|
||||
result = ClipProcessPaused;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (result == ClipProcessStarted || result == ClipProcessCopyFrame) {
|
||||
@ -1085,14 +1108,17 @@ bool ClipReadManager::handleProcessResult(ClipReaderPrivate *reader, ClipProcess
|
||||
frame->clear();
|
||||
frame->pix = reader->frame()->pix;
|
||||
frame->original = reader->frame()->original;
|
||||
frame->displayed = false;
|
||||
it.key()->moveToNextWrite();
|
||||
frame->displayed.storeRelease(0);
|
||||
if (result == ClipProcessStarted) {
|
||||
reader->_nextFrameWhen = ms;
|
||||
it.key()->moveToNextWrite();
|
||||
emit callback(it.key(), it.key()->threadIndex(), ClipReaderReinit);
|
||||
}
|
||||
} else if (result == ClipProcessPaused) {
|
||||
it.key()->moveToNextWrite();
|
||||
emit callback(it.key(), it.key()->threadIndex(), ClipReaderReinit);
|
||||
} else if (result == ClipProcessRepaint) {
|
||||
it.key()->moveToNextWrite();
|
||||
emit callback(it.key(), it.key()->threadIndex(), ClipReaderRepaint);
|
||||
}
|
||||
return true;
|
||||
@ -1174,7 +1200,7 @@ void ClipReadManager::process() {
|
||||
return;
|
||||
}
|
||||
ms = getms();
|
||||
i.value() = reader->_nextUpdateMs ? reader->_nextUpdateMs : (ms + 86400 * 1000ULL);
|
||||
i.value() = reader->_nextFrameWhen ? reader->_nextFrameWhen : (ms + 86400 * 1000ULL);
|
||||
}
|
||||
if (!reader->_paused && i.value() < minms) {
|
||||
minms = i.value();
|
||||
|
@ -497,8 +497,9 @@ enum ClipReaderNotification {
|
||||
};
|
||||
|
||||
enum ClipReaderSteps {
|
||||
FirstFrameNotReadStep = -2,
|
||||
WaitingForRequestStep = -1,
|
||||
WaitingForDimensionsStep = -3, // before ClipReaderPrivate read the first image and got the original frame size
|
||||
WaitingForRequestStep = -2, // before ClipReader got the original frame size and prepared the frame request
|
||||
WaitingForFirstFrameStep = -1, // before ClipReaderPrivate got the frame request and started waiting for the 1-2 delay
|
||||
};
|
||||
|
||||
class ClipReaderPrivate;
|
||||
@ -528,7 +529,7 @@ public:
|
||||
}
|
||||
bool currentDisplayed() const {
|
||||
Frame *frame = frameToShow();
|
||||
return frame ? frame->displayed : true;
|
||||
return frame ? (frame->displayed.loadAcquire() != 0) : true;
|
||||
}
|
||||
bool paused() const {
|
||||
return _paused.loadAcquire();
|
||||
@ -542,7 +543,8 @@ public:
|
||||
|
||||
ClipState state() const;
|
||||
bool started() const {
|
||||
return _step.loadAcquire() >= 0;
|
||||
int32 step = _step.loadAcquire();
|
||||
return (step == WaitingForFirstFrameStep) || (step >= 0);
|
||||
}
|
||||
bool ready() const;
|
||||
|
||||
@ -561,7 +563,7 @@ private:
|
||||
|
||||
mutable QAtomicInt _step; // -2, -1 - init, 0-5 - work, show ((state + 1) / 2) % 3 state, write ((state + 3) / 2) % 3
|
||||
struct Frame {
|
||||
Frame() : displayed(false), when(0) {
|
||||
Frame() : displayed(false) {
|
||||
}
|
||||
void clear() {
|
||||
pix = QPixmap();
|
||||
@ -570,13 +572,12 @@ private:
|
||||
QPixmap pix;
|
||||
QImage original;
|
||||
ClipFrameRequest request;
|
||||
bool displayed;
|
||||
uint64 when;
|
||||
QAtomicInt displayed;
|
||||
};
|
||||
mutable Frame _frames[3];
|
||||
Frame *frameToShow() const; // 0 means not ready
|
||||
Frame *frameToShow(int32 *index = 0) const; // 0 means not ready
|
||||
Frame *frameToWrite(int32 *index = 0) const; // 0 means not ready
|
||||
Frame *frameToWriteNext(bool check) const;
|
||||
Frame *frameToWriteNext(bool check, int32 *index = 0) const;
|
||||
void moveToNextShow() const;
|
||||
void moveToNextWrite() const;
|
||||
|
||||
|
@ -2630,6 +2630,8 @@ HistoryWidget::HistoryWidget(QWidget *parent) : TWidget(parent)
|
||||
, _migrated(0)
|
||||
, _history(0)
|
||||
, _histInited(false)
|
||||
, _lastScroll(0)
|
||||
, _lastScrolled(0)
|
||||
, _toHistoryEnd(this, st::historyToEnd)
|
||||
, _collapseComments(this)
|
||||
, _attachMention(this)
|
||||
@ -2722,6 +2724,9 @@ HistoryWidget::HistoryWidget(QWidget *parent) : TWidget(parent)
|
||||
connect(audioCapture(), SIGNAL(onDone(QByteArray,qint32)), this, SLOT(onRecordDone(QByteArray,qint32)));
|
||||
}
|
||||
|
||||
_updateHistoryItems.setSingleShot(true);
|
||||
connect(&_updateHistoryItems, SIGNAL(timeout()), this, SLOT(onUpdateHistoryItems()));
|
||||
|
||||
_scrollTimer.setSingleShot(false);
|
||||
|
||||
_sendActionStopTimer.setSingleShot(true);
|
||||
@ -3508,6 +3513,8 @@ void HistoryWidget::showHistory(const PeerId &peerId, MsgId showAtMsgId, bool re
|
||||
_scroll.setWidget(_list);
|
||||
_list->show();
|
||||
|
||||
_updateHistoryItems.stop();
|
||||
|
||||
if (_history->lastWidth || _history->isReadyFor(_showAtMsgId, _fixedInScrollMsgId, _fixedInScrollMsgTop)) {
|
||||
_fixedInScrollMsgId = 0;
|
||||
_fixedInScrollMsgTop = 0;
|
||||
@ -4360,6 +4367,11 @@ void HistoryWidget::onListScroll() {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (st != _lastScroll) {
|
||||
_lastScrolled = getms();
|
||||
_lastScroll = st;
|
||||
}
|
||||
}
|
||||
|
||||
void HistoryWidget::onVisibleChanged() {
|
||||
@ -4407,8 +4419,9 @@ void HistoryWidget::onSend(bool ctrlShiftEnter, MsgId replyTo) {
|
||||
_saveDraftStart = getms();
|
||||
onDraftSave();
|
||||
|
||||
onCheckMentionDropdown();
|
||||
if (!_attachMention.isHidden()) _attachMention.hideStart();
|
||||
if (!_attachType.isHidden()) _attachType.hideStart();
|
||||
if (!_emojiPan.isHidden()) _emojiPan.hideStart();
|
||||
|
||||
if (replyTo < 0) cancelReply(lastKeyboardUsed);
|
||||
if (_previewData && _previewData->pendingTill) previewCancel();
|
||||
@ -5834,7 +5847,21 @@ bool HistoryWidget::isItemVisible(HistoryItem *item) {
|
||||
|
||||
void HistoryWidget::ui_repaintHistoryItem(const HistoryItem *item) {
|
||||
if (_peer && _list && (item->history() == _history || (_migrated && item->history() == _migrated))) {
|
||||
_list->repaintItem(item);
|
||||
uint64 ms = getms();
|
||||
if (_lastScrolled + 100 <= ms) {
|
||||
_list->repaintItem(item);
|
||||
} else {
|
||||
_updateHistoryItems.start(_lastScrolled + 100 - ms);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void HistoryWidget::onUpdateHistoryItems() {
|
||||
uint64 ms = getms();
|
||||
if (_lastScrolled + 100 <= ms) {
|
||||
_list->update();
|
||||
} else {
|
||||
_updateHistoryItems.start(_lastScrolled + 100 - ms);
|
||||
}
|
||||
}
|
||||
|
||||
@ -6459,8 +6486,9 @@ void HistoryWidget::onInlineResultSend(InlineResult *result, UserData *bot) {
|
||||
Local::writeRecentHashtagsAndBots();
|
||||
}
|
||||
|
||||
onCheckMentionDropdown();
|
||||
if (!_attachMention.isHidden()) _attachMention.hideStart();
|
||||
if (!_attachType.isHidden()) _attachType.hideStart();
|
||||
if (!_emojiPan.isHidden()) _emojiPan.hideStart();
|
||||
|
||||
_field.setFocus();
|
||||
}
|
||||
|
@ -682,6 +682,8 @@ public slots:
|
||||
void onRecordDone(QByteArray result, qint32 samples);
|
||||
void onRecordUpdate(qint16 level, qint32 samples);
|
||||
|
||||
void onUpdateHistoryItems();
|
||||
|
||||
private:
|
||||
|
||||
MsgId _replyToId;
|
||||
@ -771,6 +773,10 @@ private:
|
||||
History *_migrated, *_history;
|
||||
bool _histInited; // initial updateListSize() called
|
||||
|
||||
int32 _lastScroll;
|
||||
uint64 _lastScrolled;
|
||||
QTimer _updateHistoryItems; // gifs optimization
|
||||
|
||||
IconedButton _toHistoryEnd;
|
||||
CollapseButton _collapseComments;
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user