fixed gif frame delays, no-repaint-on-scroll optimization done for history

This commit is contained in:
John Preston 2016-01-03 16:01:14 +08:00
parent 5a87bf4114
commit 85bb526294
4 changed files with 124 additions and 63 deletions

View File

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

View File

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

View File

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

View File

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