pausing gifs that are not currently displayed

This commit is contained in:
John Preston 2015-12-16 17:39:26 +03:00
parent 322eef660e
commit fdb93f700d
4 changed files with 75 additions and 18 deletions

View File

@ -2441,7 +2441,7 @@ namespace App {
}
void stopGifItems() {
while (!::gifItems.isEmpty()) {
if (!::gifItems.isEmpty()) {
if (HistoryItem *playing = ::gifItems.begin().value()) {
if (playing->getMedia() && playing->getMedia()->type() == MediaTypeGif) {
static_cast<HistoryGif*>(playing->getMedia())->stop(playing);

View File

@ -83,8 +83,9 @@ enum {
LocalEncryptKeySize = 256, // 2048 bit
AnimationTimerDelta = 7,
ClipThreadsCount = 8,
ClipThreadsCount = 4,
AverageGifSize = 320 * 240,
WaitBeforeGifPause = 200, // wait 200ms for gif draw before pausing it
SaveRecentEmojisTimeout = 3000, // 3 secs
SaveWindowPositionTimeout = 1000, // 1 sec

View File

@ -330,6 +330,8 @@ ClipReader::ClipReader(const FileLocation &location, const QByteArray &data) : _
, _width(0)
, _height(0)
, _currentDisplayed(1)
, _paused(0)
, _lastDisplayMs(getms())
, _private(0) {
if (_clipThreads.size() < ClipThreadsCount) {
_threadIndex = _clipThreads.size();
@ -365,8 +367,15 @@ void ClipReader::start(int32 framew, int32 frameh, int32 outerw, int32 outerh, b
}
QPixmap ClipReader::current(int32 framew, int32 frameh, int32 outerw, int32 outerh, uint64 ms) {
_currentDisplayed.storeRelease(1);
_lastDisplayMs = ms;
_lastDisplayMs.set(ms);
_currentDisplayed.set(true);
if (_paused.get()) {
_paused.set(false);
if (_clipManagers.size() <= _threadIndex) error();
if (_state != ClipError) {
_clipManagers.at(_threadIndex)->update(this);
}
}
int32 factor(cIntRetinaFactor());
QPixmap result(_current);
@ -441,8 +450,10 @@ public:
, _accessed(false)
, _buffer(_data.isEmpty() ? 0 : &_data)
, _reader(0)
, _previousMs(0)
, _currentMs(0)
, _nextUpdateMs(0) {
, _nextUpdateMs(0)
, _paused(false) {
if (_data.isEmpty() && !_location->accessEnable()) {
error();
@ -476,6 +487,7 @@ public:
if (_current.isNull()) { // first frame read, but not yet prepared
_currentOriginal.setDevicePixelRatio(_request.factor);
_previousMs = _currentMs;
_currentMs = ms;
_current = _prepareFrame(_request, _currentOriginal, _currentCache, true);
@ -483,7 +495,7 @@ public:
return error();
}
return ClipProcessStarted;
} else if (ms >= _nextUpdateMs) {
} else if (!_paused && ms >= _nextUpdateMs) {
swapBuffers();
return ClipProcessRedraw;
}
@ -508,6 +520,7 @@ public:
}
void swapBuffers(uint64 ms = 0) {
_previousMs = _currentMs;
_currentMs = qMax(ms, _nextUpdateMs);
qSwap(_currentOriginal, _nextOriginal);
qSwap(_current, _next);
@ -628,13 +641,15 @@ private:
QImage _currentOriginal, _nextOriginal, _currentCache, _nextCache;
int32 _framesLeft;
uint64 _currentMs, _nextUpdateMs;
uint64 _previousMs, _currentMs, _nextUpdateMs;
bool _paused;
friend class ClipReadManager;
};
ClipReadManager::ClipReadManager(QThread *thread) : _processingInThread(0) {
ClipReadManager::ClipReadManager(QThread *thread) : _processingInThread(0), _needReProcess(false) {
moveToThread(thread);
connect(thread, SIGNAL(started()), this, SLOT(process()));
connect(this, SIGNAL(processDelayed()), this, SLOT(process()), Qt::QueuedConnection);
@ -669,7 +684,7 @@ void ClipReadManager::stop(ClipReader *reader) {
emit processDelayed();
}
bool ClipReadManager::handleProcessResult(ClipReaderPrivate *reader, ClipProcessResult result) {
bool ClipReadManager::handleProcessResult(ClipReaderPrivate *reader, ClipProcessResult result, uint64 ms) {
QMutexLocker lock(&_readerPointersMutex);
ReaderPointers::iterator it = _readerPointers.find(reader->_interface);
if (result == ClipProcessError) {
@ -686,10 +701,20 @@ bool ClipReadManager::handleProcessResult(ClipReaderPrivate *reader, ClipProcess
if (result == ClipProcessStarted) {
_loadLevel.fetchAndAddRelease(reader->_currentOriginal.width() * reader->_currentOriginal.height() - AverageGifSize);
}
if (!reader->_paused && (result == ClipProcessRedraw || result == ClipProcessWait)) {
if (it.key()->_lastDisplayMs.get() + WaitBeforeGifPause < qMax(reader->_previousMs, ms)) {
reader->_paused = true;
it.key()->_paused.set(true);
if (it.key()->_lastDisplayMs.get() + WaitBeforeGifPause >= qMax(reader->_previousMs, ms)) {
it.key()->_paused.set(false);
reader->_paused = false;
}
}
}
if (result == ClipProcessReinit || result == ClipProcessRedraw || result == ClipProcessStarted) {
it.key()->_current = reader->_current;
it.key()->_currentOriginal = reader->_currentOriginal;
it.key()->_currentDisplayed.storeRelease(0);
it.key()->_currentDisplayed.set(false);
if (result == ClipProcessReinit) {
emit reinit(it.key());
} else if (result == ClipProcessRedraw) {
@ -700,7 +725,7 @@ bool ClipReadManager::handleProcessResult(ClipReaderPrivate *reader, ClipProcess
}
ClipReadManager::ResultHandleState ClipReadManager::handleResult(ClipReaderPrivate *reader, ClipProcessResult result, uint64 ms) {
if (!handleProcessResult(reader, result)) {
if (!handleProcessResult(reader, result, ms)) {
_loadLevel.fetchAndAddRelease(-1 * (reader->_currentOriginal.isNull() ? AverageGifSize : reader->_currentOriginal.width() * reader->_currentOriginal.height()));
delete reader;
return ResultHandleRemove;
@ -719,7 +744,10 @@ ClipReadManager::ResultHandleState ClipReadManager::handleResult(ClipReaderPriva
}
void ClipReadManager::process() {
if (_processingInThread) return;
if (_processingInThread) {
_needReProcess = true;
return;
}
_timer.stop();
_processingInThread = thread();
@ -734,6 +762,9 @@ void ClipReadManager::process() {
_readers.insert(i.value(), 0);
} else {
it.value() = ms;
if (it.key()->_paused && !i.key()->_paused.get()) {
it.key()->_paused = false;
}
}
i.value()->_request = i.key()->_request;
i.value() = 0;
@ -754,15 +785,17 @@ void ClipReadManager::process() {
return;
}
i.value() = i.key()->_nextUpdateMs;
ms = getms();
}
if (i.value() < minms) {
if (!i.key()->_paused && i.value() < minms) {
minms = i.value();
}
++i;
}
ms = getms();
if (minms <= ms) {
if (_needReProcess || minms <= ms) {
_needReProcess = false;
_timer.start(1);
} else {
_timer.start(minms - ms);

View File

@ -493,6 +493,28 @@ struct ClipFrameRequest {
bool rounded;
};
template <typename Type>
class Atomic {
public:
Atomic(const Type &value = Type()) : _v(1, value) {
}
Type get() const {
QVector<Type> v(_v);
return v.at(0);
}
void set(const Type &value) {
QVector<Type> v(1, value);
_v = v;
}
private:
QVector<Type> _v;
};
class ClipReaderPrivate;
class ClipReader {
public:
@ -502,7 +524,7 @@ public:
void start(int32 framew, int32 frameh, int32 outerw, int32 outerh, bool rounded);
QPixmap current(int32 framew, int32 frameh, int32 outerw, int32 outerh, uint64 ms);
bool currentDisplayed() const {
return _currentDisplayed.loadAcquire() > 0;
return _currentDisplayed.get();
}
int32 width() const;
@ -529,8 +551,8 @@ private:
QPixmap _current;
QImage _currentOriginal, _cacheForResize;
QAtomicInt _currentDisplayed;
uint64 _lastDisplayMs;
Atomic<bool> _currentDisplayed, _paused;
Atomic<uint64> _lastDisplayMs;
int32 _threadIndex;
friend class ClipReadManager;
@ -580,7 +602,7 @@ private:
ReaderPointers _readerPointers;
QMutex _readerPointersMutex;
bool handleProcessResult(ClipReaderPrivate *reader, ClipProcessResult result);
bool handleProcessResult(ClipReaderPrivate *reader, ClipProcessResult result, uint64 ms);
enum ResultHandleState {
ResultHandleRemove,
@ -594,5 +616,6 @@ private:
QTimer _timer;
QThread *_processingInThread;
bool _needReProcess;
};