Use Lottie::MultiPlayer in StickerSetBox.

This commit is contained in:
John Preston 2019-07-02 18:03:09 +02:00
parent 0dd1b4eae6
commit a4fbbc06d1
3 changed files with 79 additions and 36 deletions

View file

@ -21,7 +21,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "ui/image/image.h" #include "ui/image/image.h"
#include "ui/text/text_utilities.h" #include "ui/text/text_utilities.h"
#include "ui/emoji_config.h" #include "ui/emoji_config.h"
#include "lottie/lottie_single_player.h" #include "lottie/lottie_multi_player.h"
#include "lottie/lottie_animation.h"
#include "window/window_session_controller.h" #include "window/window_session_controller.h"
#include "auth_session.h" #include "auth_session.h"
#include "apiwrap.h" #include "apiwrap.h"
@ -65,10 +66,12 @@ protected:
private: private:
struct Element { struct Element {
not_null<DocumentData*> document; not_null<DocumentData*> document;
std::unique_ptr<Lottie::SinglePlayer> animated; Lottie::Animation *animated = nullptr;
Ui::Animations::Simple overAnimation; Ui::Animations::Simple overAnimation;
}; };
void visibleTopBottomUpdated(int visibleTop, int visibleBottom) override;
QSize boundingBoxSize() const; QSize boundingBoxSize() const;
void paintSticker(Painter &p, int index, QPoint position) const; void paintSticker(Painter &p, int index, QPoint position) const;
@ -86,11 +89,14 @@ private:
return (_setFlags & MTPDstickerSet::Flag::f_masks); return (_setFlags & MTPDstickerSet::Flag::f_masks);
} }
not_null<Lottie::MultiPlayer*> getLottiePlayer();
void showPreview(); void showPreview();
not_null<Window::SessionController*> _controller; not_null<Window::SessionController*> _controller;
MTP::Sender _mtp; MTP::Sender _mtp;
std::vector<Element> _elements; std::vector<Element> _elements;
std::unique_ptr<Lottie::MultiPlayer> _lottiePlayer;
Stickers::Pack _pack; Stickers::Pack _pack;
Stickers::ByEmojiMap _emoji; Stickers::ByEmojiMap _emoji;
bool _loaded = false; bool _loaded = false;
@ -472,6 +478,18 @@ void StickerSetBox::Inner::showPreview() {
} }
} }
not_null<Lottie::MultiPlayer*> StickerSetBox::Inner::getLottiePlayer() {
if (!_lottiePlayer) {
_lottiePlayer = std::make_unique<Lottie::MultiPlayer>(
Lottie::MakeFrameRenderer());
_lottiePlayer->updates(
) | rpl::start_with_next([=] {
update();
}, lifetime());
}
return _lottiePlayer.get();
}
int32 StickerSetBox::Inner::stickerFromGlobalPos(const QPoint &p) const { int32 StickerSetBox::Inner::stickerFromGlobalPos(const QPoint &p) const {
QPoint l(mapFromGlobal(p)); QPoint l(mapFromGlobal(p));
if (rtl()) l.setX(width() - l.x()); if (rtl()) l.setX(width() - l.x());
@ -492,7 +510,8 @@ void StickerSetBox::Inner::paintEvent(QPaintEvent *e) {
return; return;
} }
int32 rows = _elements.size() / kStickersPanelPerRow + ((_elements.size() % kStickersPanelPerRow) ? 1 : 0); int32 rows = (_elements.size() / kStickersPanelPerRow)
+ ((_elements.size() % kStickersPanelPerRow) ? 1 : 0);
int32 from = qFloor(e->rect().top() / st::stickersSize.height()), to = qFloor(e->rect().bottom() / st::stickersSize.height()) + 1; int32 from = qFloor(e->rect().top() / st::stickersSize.height()), to = qFloor(e->rect().bottom() / st::stickersSize.height()) + 1;
for (int32 i = from; i < to; ++i) { for (int32 i = from; i < to; ++i) {
@ -505,6 +524,14 @@ void StickerSetBox::Inner::paintEvent(QPaintEvent *e) {
paintSticker(p, index, pos); paintSticker(p, index, pos);
} }
} }
if (_lottiePlayer) {
const auto paused = _controller->isGifPausedAtLeastFor(
Window::GifPauseReason::Layer);
if (!paused) {
_lottiePlayer->markFrameShown();
}
}
} }
QSize StickerSetBox::Inner::boundingBoxSize() const { QSize StickerSetBox::Inner::boundingBoxSize() const {
@ -513,20 +540,56 @@ QSize StickerSetBox::Inner::boundingBoxSize() const {
st::stickersSize.height() - st::buttonRadius * 2); st::stickersSize.height() - st::buttonRadius * 2);
} }
void StickerSetBox::Inner::visibleTopBottomUpdated(
int visibleTop,
int visibleBottom) {
const auto pauseInRows = [&](int fromRow, int tillRow) {
Expects(fromRow <= tillRow);
for (auto i = fromRow; i != tillRow; ++i) {
for (auto j = 0; j != kStickersPanelPerRow; ++j) {
const auto index = i * kStickersPanelPerRow + j;
if (index >= _elements.size()) {
break;
}
if (const auto animated = _elements[index].animated) {
_lottiePlayer->pause(animated);
}
}
}
};
const auto count = int(_elements.size());
const auto rowsCount = (count / kStickersPanelPerRow)
+ ((count % kStickersPanelPerRow) ? 1 : 0);
const auto rowsTop = st::stickersPadding.top();
const auto singleHeight = st::stickersSize.height();
const auto rowsBottom = rowsTop + rowsCount * singleHeight;
if (visibleTop >= rowsTop + singleHeight && visibleTop < rowsBottom) {
const auto pauseHeight = (visibleTop - rowsTop);
const auto pauseRows = std::min(
pauseHeight / singleHeight,
rowsCount);
pauseInRows(0, pauseRows);
}
if (visibleBottom > rowsTop
&& visibleBottom + singleHeight <= rowsBottom) {
const auto pauseHeight = (rowsBottom - visibleBottom);
const auto pauseRows = std::min(
pauseHeight / singleHeight,
rowsCount);
pauseInRows(rowsCount - pauseRows, rowsCount);
}
}
void StickerSetBox::Inner::setupLottie(int index) { void StickerSetBox::Inner::setupLottie(int index) {
auto &element = _elements[index]; auto &element = _elements[index];
const auto document = element.document; const auto document = element.document;
element.animated = Stickers::LottiePlayerFromDocument( element.animated = Stickers::LottieAnimationFromDocument(
getLottiePlayer(),
document, document,
Stickers::LottieSize::StickerSet, Stickers::LottieSize::StickerSet,
boundingBoxSize() * cIntRetinaFactor()); boundingBoxSize() * cIntRetinaFactor());
const auto animation = element.animated.get();
animation->updates(
) | rpl::start_with_next([=] {
update();
}, lifetime());
} }
void StickerSetBox::Inner::paintSticker( void StickerSetBox::Inner::paintSticker(
@ -558,15 +621,12 @@ void StickerSetBox::Inner::paintSticker(
if (h < 1) h = 1; if (h < 1) h = 1;
QPoint ppos = position + QPoint((st::stickersSize.width() - w) / 2, (st::stickersSize.height() - h) / 2); QPoint ppos = position + QPoint((st::stickersSize.width() - w) / 2, (st::stickersSize.height() - h) / 2);
if (element.animated && element.animated->ready()) { if (element.animated && element.animated->ready()) {
const auto paused = _controller->isGifPausedAtLeastFor(
Window::GifPauseReason::Layer);
if (!paused) {
element.animated->markFrameShown();
}
const auto frame = element.animated->frame(); const auto frame = element.animated->frame();
p.drawImage( p.drawImage(
QRect(ppos, frame.size() / cIntRetinaFactor()), QRect(ppos, frame.size() / cIntRetinaFactor()),
frame); frame);
_lottiePlayer->unpause(element.animated);
} else if (const auto image = document->getStickerSmall()) { } else if (const auto image = document->getStickerSmall()) {
p.drawPixmapLeft( p.drawPixmapLeft(
ppos, ppos,

View file

@ -98,7 +98,7 @@ void MultiPlayer::start(
if (_active.empty() if (_active.empty()
|| (_lastSyncTime == kTimeUnknown || (_lastSyncTime == kTimeUnknown
&& _nextFrameTime == kTimeUnknown)) { && _nextFrameTime == kTimeUnknown)) {
startBeforeLifeCycle(animation, std::move(info)); addNewToActive(animation, std::move(info));
} else { } else {
// We always try to mark as shown at the same time, so we start a new // We always try to mark as shown at the same time, so we start a new
// animation at the same time we mark all existing as shown. // animation at the same time we mark all existing as shown.
@ -107,7 +107,7 @@ void MultiPlayer::start(
_updates.fire({}); _updates.fire({});
} }
void MultiPlayer::startBeforeLifeCycle( void MultiPlayer::addNewToActive(
not_null<Animation*> animation, not_null<Animation*> animation,
StartingInfo &&info) { StartingInfo &&info) {
_active.emplace(animation, info.state.get()); _active.emplace(animation, info.state.get());
@ -117,20 +117,6 @@ void MultiPlayer::startBeforeLifeCycle(
} }
} }
void MultiPlayer::startInsideLifeCycle(
not_null<Animation*> animation,
StartingInfo &&info) {
const auto state = info.state.get();
if (info.paused) {
_paused.emplace(
animation,
PausedInfo{ state, _lastSyncTime, _delay });
} else {
_active.emplace(animation, state);
}
startAtRightTime(std::move(info.state));
}
void MultiPlayer::processPending() { void MultiPlayer::processPending() {
Expects(_lastSyncTime != kTimeUnknown); Expects(_lastSyncTime != kTimeUnknown);
@ -141,7 +127,7 @@ void MultiPlayer::processPending() {
unpauseAndKeepUp(animation); unpauseAndKeepUp(animation);
} }
for (auto &[animation, info] : base::take(_pendingToStart)) { for (auto &[animation, info] : base::take(_pendingToStart)) {
startInsideLifeCycle(animation, std::move(info)); addNewToActive(animation, std::move(info));
} }
for (const auto &animation : base::take(_pendingRemove)) { for (const auto &animation : base::take(_pendingRemove)) {
removeNow(animation); removeNow(animation);

View file

@ -69,10 +69,7 @@ private:
bool paused = false; bool paused = false;
}; };
void startBeforeLifeCycle( void addNewToActive(
not_null<Animation*> animation,
StartingInfo &&info);
void startInsideLifeCycle(
not_null<Animation*> animation, not_null<Animation*> animation,
StartingInfo &&info); StartingInfo &&info);
[[nodiscard]] int countFrameIndex( [[nodiscard]] int countFrameIndex(