diff --git a/Telegram/SourceFiles/gui/animation.cpp b/Telegram/SourceFiles/gui/animation.cpp index e373f58a44..8af77652ae 100644 --- a/Telegram/SourceFiles/gui/animation.cpp +++ b/Telegram/SourceFiles/gui/animation.cpp @@ -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(); diff --git a/Telegram/SourceFiles/gui/animation.h b/Telegram/SourceFiles/gui/animation.h index d6edea257f..b3aa52afa0 100644 --- a/Telegram/SourceFiles/gui/animation.h +++ b/Telegram/SourceFiles/gui/animation.h @@ -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; diff --git a/Telegram/SourceFiles/historywidget.cpp b/Telegram/SourceFiles/historywidget.cpp index a635b3fa2d..e610734fa3 100644 --- a/Telegram/SourceFiles/historywidget.cpp +++ b/Telegram/SourceFiles/historywidget.cpp @@ -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(); } diff --git a/Telegram/SourceFiles/historywidget.h b/Telegram/SourceFiles/historywidget.h index 826c84556e..24a9bdad5b 100644 --- a/Telegram/SourceFiles/historywidget.h +++ b/Telegram/SourceFiles/historywidget.h @@ -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;