animations rewritten on atomics

This commit is contained in:
John Preston 2016-01-01 04:07:41 +08:00
parent 82fb3f590f
commit d876861431
3 changed files with 136 additions and 64 deletions

View File

@ -225,10 +225,8 @@ ClipReader::ClipReader(const FileLocation &location, const QByteArray &data, Cal
, _state(ClipReading) , _state(ClipReading)
, _width(0) , _width(0)
, _height(0) , _height(0)
, _currentDisplayed(1) , _step(FirstFrameNotReadStep)
, _paused(0) , _paused(0)
, _lastDisplayMs(0)
, _startDisplayMs(getms())
, _autoplay(false) , _autoplay(false)
, _private(0) { , _private(0) {
if (_clipThreads.size() < ClipThreadsCount) { if (_clipThreads.size() < ClipThreadsCount) {
@ -250,6 +248,48 @@ ClipReader::ClipReader(const FileLocation &location, const QByteArray &data, Cal
_clipManagers.at(_threadIndex)->append(this, location, data); _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() const { // 0 means not ready
int32 step = _step.loadAcquire();
if (step == FirstFrameNotReadStep) {
return _frames;
} else if (step == WaitingForRequestStep) {
return 0;
}
return _frames + (((step + 3) / 2) % 3);
}
ClipReader::Frame *ClipReader::frameToRequestOther() const {
int32 step = _step.loadAcquire();
if (step == FirstFrameNotReadStep || step == WaitingForRequestStep) {
return 0;
}
return _frames + (((step + 5) / 2) % 3);
}
void ClipReader::moveToNextShow() const {
int32 step = _step.loadAcquire();
if (step % 2) {
_step.storeRelease((step + 1) % 6);
}
}
void ClipReader::moveToNextWrite() const {
int32 step = _step.loadAcquire();
if (!(step % 2)) {
_step.storeRelease(step + 1);
}
}
void ClipReader::callback(ClipReader *reader, int32 threadIndex, ClipReaderNotification notification) { void ClipReader::callback(ClipReader *reader, int32 threadIndex, ClipReaderNotification notification) {
// check if reader is not deleted already // check if reader is not deleted already
if (_clipManagers.size() > threadIndex && _clipManagers.at(threadIndex)->carries(reader)) { if (_clipManagers.size() > threadIndex && _clipManagers.at(threadIndex)->carries(reader)) {
@ -261,20 +301,28 @@ void ClipReader::start(int32 framew, int32 frameh, int32 outerw, int32 outerh, b
if (_clipManagers.size() <= _threadIndex) error(); if (_clipManagers.size() <= _threadIndex) error();
if (_state == ClipError) return; if (_state == ClipError) return;
if (_step.loadAcquire() == WaitingForRequestStep) {
int32 factor(cIntRetinaFactor()); int32 factor(cIntRetinaFactor());
_request.factor = factor; ClipFrameRequest request;
_request.framew = framew * factor; request.factor = factor;
_request.frameh = frameh * factor; request.framew = framew * factor;
_request.outerw = outerw * factor; request.frameh = frameh * factor;
_request.outerh = outerh * factor; request.outerw = outerw * factor;
_request.rounded = rounded; request.outerh = outerh * factor;
request.rounded = rounded;
_frames[0].request = _frames[1].request = _frames[2].request = request;
_step.storeRelease(0); // start working
_clipManagers.at(_threadIndex)->start(this); _clipManagers.at(_threadIndex)->start(this);
}
} }
QPixmap ClipReader::current(int32 framew, int32 frameh, int32 outerw, int32 outerh, uint64 ms) { QPixmap ClipReader::current(int32 framew, int32 frameh, int32 outerw, int32 outerh, uint64 ms) {
_currentDisplayed.store(1); Frame *frame = frameToShow();
t_assert(frame != 0);
frame->displayed = true;
if (ms) { if (ms) {
_lastDisplayMs.storeRelease(int32(ms - _startDisplayMs.get())); frame->when = ms;
if (_paused.loadAcquire()) { if (_paused.loadAcquire()) {
_paused.storeRelease(0); _paused.storeRelease(0);
if (_clipManagers.size() <= _threadIndex) error(); if (_clipManagers.size() <= _threadIndex) error();
@ -285,39 +333,44 @@ QPixmap ClipReader::current(int32 framew, int32 frameh, int32 outerw, int32 oute
} }
int32 factor(cIntRetinaFactor()); int32 factor(cIntRetinaFactor());
QPixmap result(_current); if (frame->pix.width() == outerw * factor && frame->pix.height() == outerh * factor) {
if (result.width() == outerw * factor && result.height() == outerh * factor) { moveToNextShow();
return result; return frame->pix;
} }
_request.framew = framew * factor; frame->request.framew = framew * factor;
_request.frameh = frameh * factor; frame->request.frameh = frameh * factor;
_request.outerw = outerw * factor; frame->request.outerw = outerw * factor;
_request.outerh = outerh * factor; frame->request.outerh = outerh * factor;
frame->pix = QPixmap();
QImage current(_currentOriginal); QImage cache;
current.setDevicePixelRatio(cRetinaFactor()); frame->pix = _prepareFrame(frame->request, frame->original, cache, true);
result = _current = QPixmap(); Frame *other = frameToRequestOther();
result = _current = _prepareFrame(_request, current, _cacheForResize, true); t_assert(other != 0);
other->request = frame->request;
moveToNextShow();
if (_clipManagers.size() <= _threadIndex) error(); if (_clipManagers.size() <= _threadIndex) error();
if (_state != ClipError) { if (_state != ClipError) {
_clipManagers.at(_threadIndex)->update(this); _clipManagers.at(_threadIndex)->update(this);
} }
return result; return frame->pix;
} }
bool ClipReader::ready() const { bool ClipReader::ready() const {
if (_width && _height) return true; if (_width && _height) return true;
QImage first(_currentOriginal); const Frame *frame = frameToShow();
if (first.isNull()) return false; if (frame) {
_width = frame->original.width();
_width = first.width(); _height = frame->original.height();
_height = first.height();
return true; return true;
}
return false;
} }
int32 ClipReader::width() const { int32 ClipReader::width() const {
@ -1003,25 +1056,21 @@ bool ClipReadManager::handleProcessResult(ClipReaderPrivate *reader, ClipProcess
_loadLevel.fetchAndAddRelaxed(reader->_width * reader->_height - AverageGifSize); _loadLevel.fetchAndAddRelaxed(reader->_width * reader->_height - AverageGifSize);
} }
if (!reader->_paused && (result == ClipProcessRepaint || result == ClipProcessWait)) { if (!reader->_paused && (result == ClipProcessRepaint || result == ClipProcessWait)) {
int32 lastDisplay = it.key()->_lastDisplayMs.loadAcquire(); ClipReader::Frame *frame = it.key()->frameToWrite(), *other = it.key()->frameToRequestOther();
if (lastDisplay > 3000 || lastDisplay < 0) { // playing more then a day t_assert(frame != 0 && other != 0);
it.key()->_startDisplayMs.set(ms); if (qMax(frame->when, other->when) + WaitBeforeGifPause < qMax(reader->_previousMs, ms)) {
it.key()->_lastDisplayMs.storeRelease(getms() - ms);
} else if (it.key()->_startDisplayMs.get() + lastDisplay + WaitBeforeGifPause < qMax(reader->_previousMs, ms)) {
reader->_paused = true; reader->_paused = true;
it.key()->_paused.storeRelease(true); it.key()->_paused.storeRelease(true);
if (it.key()->_startDisplayMs.get() + it.key()->_lastDisplayMs.loadAcquire() + WaitBeforeGifPause >= qMax(reader->_previousMs, ms)) {
it.key()->_paused.storeRelease(false);
reader->_paused = false;
} else {
result = ClipProcessReinit; result = ClipProcessReinit;
} }
} }
}
if (result == ClipProcessReinit || result == ClipProcessRepaint || result == ClipProcessStarted) { if (result == ClipProcessReinit || result == ClipProcessRepaint || result == ClipProcessStarted) {
it.key()->_current = reader->_current; ClipReader::Frame *frame = it.key()->frameToWrite();
it.key()->_currentOriginal = reader->_currentOriginal; t_assert(frame != 0);
it.key()->_currentDisplayed.store(false); frame->pix = reader->_current;
frame->original = reader->_currentOriginal;
frame->displayed = false;
it.key()->moveToNextWrite();
if (result == ClipProcessReinit) { if (result == ClipProcessReinit) {
emit callback(it.key(), it.key()->threadIndex(), ClipReaderReinit); emit callback(it.key(), it.key()->threadIndex(), ClipReaderReinit);
} else if (result == ClipProcessRepaint) { } else if (result == ClipProcessRepaint) {
@ -1073,7 +1122,8 @@ void ClipReadManager::process() {
it.key()->_paused = false; it.key()->_paused = false;
} }
} }
i.value()->_request = i.key()->_request; ClipReader::Frame *frame = i.key()->frameToWrite();
if (frame) i.value()->_request = frame->request;
i.value() = 0; i.value() = 0;
} }
} }

View File

@ -520,6 +520,11 @@ enum ClipReaderNotification {
ClipReaderRepaint, ClipReaderRepaint,
}; };
enum ClipReaderSteps {
FirstFrameNotReadStep = -2,
WaitingForRequestStep = -1,
};
class ClipReaderPrivate; class ClipReaderPrivate;
class ClipReader { class ClipReader {
public: public:
@ -538,11 +543,16 @@ public:
void start(int32 framew, int32 frameh, int32 outerw, int32 outerh, bool rounded); void start(int32 framew, int32 frameh, int32 outerw, int32 outerh, bool rounded);
QPixmap current(int32 framew, int32 frameh, int32 outerw, int32 outerh, uint64 ms); QPixmap current(int32 framew, int32 frameh, int32 outerw, int32 outerh, uint64 ms);
QImage frameOriginal() const { QPixmap frameOriginal() const {
return _currentOriginal; Frame *frame = frameToShow();
if (!frame) return QPixmap();
QPixmap result(frame ? QPixmap::fromImage(frame->original) : QPixmap());
result.detach();
return result;
} }
bool currentDisplayed() const { bool currentDisplayed() const {
return _currentDisplayed.load(); Frame *frame = frameToShow();
return frame ? frame->displayed : true;
} }
bool paused() const { bool paused() const {
return _paused.loadAcquire(); return _paused.loadAcquire();
@ -556,7 +566,7 @@ public:
ClipState state() const; ClipState state() const;
bool started() const { bool started() const {
return _request.valid(); return _step.loadAcquire() >= 0;
} }
bool ready() const; bool ready() const;
@ -571,14 +581,26 @@ private:
ClipState _state; ClipState _state;
ClipFrameRequest _request;
mutable int32 _width, _height; mutable int32 _width, _height;
QPixmap _current; mutable QAtomicInt _step; // -2, -1 - init, 0-5 - work, show ((state + 1) / 2) % 3 state, write ((state + 3) / 2) % 3
QImage _currentOriginal, _cacheForResize; struct Frame {
QAtomicInt _currentDisplayed, _paused, _lastDisplayMs; Frame() : displayed(false), when(0) {
Atomic<uint64> _startDisplayMs; }
QPixmap pix;
QImage original;
ClipFrameRequest request;
bool displayed;
uint64 when;
};
mutable Frame _frames[3];
Frame *frameToShow() const; // 0 means not ready
Frame *frameToWrite() const; // 0 means not ready
Frame *frameToRequestOther() const;
void moveToNextShow() const;
void moveToNextWrite() const;
QAtomicInt _paused;
int32 _threadIndex; int32 _threadIndex;
bool _autoplay; bool _autoplay;

View File

@ -738,7 +738,7 @@ void MediaView::onCopy() {
if (!_current.isNull()) { if (!_current.isNull()) {
QApplication::clipboard()->setPixmap(_current); QApplication::clipboard()->setPixmap(_current);
} else if (gifShown()) { } else if (gifShown()) {
QApplication::clipboard()->setPixmap(QPixmap::fromImage(_gif->frameOriginal())); QApplication::clipboard()->setPixmap(_gif->frameOriginal());
} }
} else { } else {
if (!_photo || !_photo->loaded()) return; if (!_photo || !_photo->loaded()) return;