Extract some Lottie::Animation code to Lottie::Player.

This commit is contained in:
John Preston 2019-06-28 13:33:47 +02:00
parent 4a7b5a8e01
commit cbffeca8d5
23 changed files with 363 additions and 272 deletions

View File

@ -21,7 +21,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "ui/image/image.h"
#include "ui/text/text_utilities.h"
#include "ui/emoji_config.h"
#include "lottie/lottie_animation.h"
#include "lottie/lottie_single_player.h"
#include "window/window_session_controller.h"
#include "auth_session.h"
#include "apiwrap.h"
@ -65,7 +65,7 @@ protected:
private:
struct Element {
not_null<DocumentData*> document;
std::unique_ptr<Lottie::Animation> animated;
std::unique_ptr<Lottie::SinglePlayer> animated;
Ui::Animations::Simple overAnimation;
};
@ -517,7 +517,7 @@ void StickerSetBox::Inner::setupLottie(int index) {
auto &element = _elements[index];
const auto document = element.document;
element.animated = Stickers::LottieFromDocument(
element.animated = Stickers::LottiePlayerFromDocument(
document,
Stickers::LottieSize::StickerSet,
boundingBoxSize() * cIntRetinaFactor());
@ -566,9 +566,10 @@ void StickerSetBox::Inner::paintSticker(
if (!paused) {
element.animated->markFrameShown();
}
const auto frame = element.animated->frame(request);
p.drawImage(
QRect(ppos, QSize(w, h)),
element.animated->frame(request));
QRect(ppos, frame.size() / cIntRetinaFactor()),
frame);
} else if (const auto image = document->getStickerSmall()) {
p.drawPixmapLeft(
ppos,

View File

@ -19,7 +19,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "mainwindow.h"
#include "ui/toast/toast.h"
#include "ui/emoji_config.h"
#include "lottie/lottie_animation.h"
#include "lottie/lottie_single_player.h"
#include "styles/style_chat_helpers.h"
namespace Stickers {
@ -1092,7 +1092,7 @@ RecentStickerPack &GetRecentPack() {
return cRefRecentStickers();
}
std::unique_ptr<Lottie::Animation> LottieFromDocument(
std::unique_ptr<Lottie::SinglePlayer> LottiePlayerFromDocument(
not_null<DocumentData*> document,
LottieSize sizeTag,
QSize box) {
@ -1100,9 +1100,8 @@ std::unique_ptr<Lottie::Animation> LottieFromDocument(
const auto filepath = document->filepath();
if (box.width() & box.height() > kDontCacheLottieAfterArea) {
// Don't use frame caching for large stickers.
return Lottie::FromContent(
data,
filepath,
return std::make_unique<Lottie::SinglePlayer>(
Lottie::ReadContent(data, filepath),
Lottie::FrameRequest{ box });
}
if (const auto baseKey = document->bigFileBaseCacheKey()) {
@ -1121,14 +1120,15 @@ std::unique_ptr<Lottie::Animation> LottieFromDocument(
weak->data().cacheBigFile().put(key, std::move(data));
});
};
return Lottie::FromCached(
return std::make_unique<Lottie::SinglePlayer>(
get,
put,
data,
filepath,
Lottie::ReadContent(data, filepath),
Lottie::FrameRequest{ box });
}
return Lottie::FromContent(data, filepath, Lottie::FrameRequest{ box });
return std::make_unique<Lottie::SinglePlayer>(
Lottie::ReadContent(data, filepath),
Lottie::FrameRequest{ box });
}
} // namespace Stickers

View File

@ -12,7 +12,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
class DocumentData;
namespace Lottie {
class Animation;
class SinglePlayer;
} // namespace Lottie
namespace Stickers {
@ -113,11 +113,9 @@ enum class LottieSize : uchar {
MessageHistory,
StickerSet,
StickersPanel,
StickersColumn,
MediaPreview,
};
std::unique_ptr<Lottie::Animation> LottieFromDocument(
std::unique_ptr<Lottie::SinglePlayer> LottiePlayerFromDocument(
not_null<DocumentData*> document,
LottieSize sizeTag,
QSize box);

View File

@ -14,7 +14,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "ui/effects/animations.h"
#include "ui/effects/ripple_animation.h"
#include "ui/image/image.h"
#include "lottie/lottie_animation.h"
#include "lottie/lottie_single_player.h"
#include "boxes/stickers_box.h"
#include "inline_bots/inline_bot_result.h"
#include "chat_helpers/stickers.h"
@ -1368,9 +1368,9 @@ void StickersListWidget::setupLottie(Set &set, int section, int index) {
auto &sticker = set.stickers[index];
const auto document = sticker.document;
sticker.animated = Stickers::LottieFromDocument(
sticker.animated = Stickers::LottiePlayerFromDocument(
document,
Stickers::LottieSize::StickersColumn, // #TODO stickers
Stickers::LottieSize::StickersPanel,
boundingBoxSize() * cIntRetinaFactor());
const auto animation = sticker.animated.get();
@ -1424,9 +1424,10 @@ void StickersListWidget::paintSticker(Painter &p, Set &set, int y, int section,
if (!paused) {
sticker.animated->markFrameShown();
}
const auto frame = sticker.animated->frame(request);
p.drawImage(
QRect(ppos, QSize(w, h)),
sticker.animated->frame(request));
QRect(ppos, frame.size() / cIntRetinaFactor()),
frame);
} else if (const auto image = document->getStickerSmall()) {
if (image->loaded()) {
p.drawPixmapLeft(

View File

@ -22,7 +22,7 @@ class RippleAnimation;
} // namespace Ui
namespace Lottie {
class Animation;
class SinglePlayer;
} // namespace Lottie
namespace ChatHelpers {
@ -98,20 +98,22 @@ private:
};
struct OverSticker {
int section;
int index;
bool overDelete;
int section = 0;
int index = 0;
bool overDelete = false;
};
struct OverSet {
int section;
int section = 0;
};
struct OverButton {
int section;
int section = 0;
};
struct OverGroupAdd {
};
friend inline bool operator==(OverSticker a, OverSticker b) {
return (a.section == b.section) && (a.index == b.index) && (a.overDelete == b.overDelete);
return (a.section == b.section)
&& (a.index == b.index)
&& (a.overDelete == b.overDelete);
}
friend inline bool operator==(OverSet a, OverSet b) {
return (a.section == b.section);
@ -122,7 +124,11 @@ private:
friend inline bool operator==(OverGroupAdd a, OverGroupAdd b) {
return true;
}
using OverState = base::optional_variant<OverSticker, OverSet, OverButton, OverGroupAdd>;
using OverState = base::optional_variant<
OverSticker,
OverSet,
OverButton,
OverGroupAdd>;
struct SectionInfo {
int section = 0;
@ -135,7 +141,7 @@ private:
struct Sticker {
not_null<DocumentData*> document;
std::unique_ptr<Lottie::Animation> animated;
std::unique_ptr<Lottie::SinglePlayer> animated;
};
struct Set {

View File

@ -45,14 +45,16 @@ TabbedPanel::TabbedPanel(
, _maxContentHeight(st::emojiPanMaxHeight) {
_selector->setParent(this);
_selector->setRoundRadius(st::buttonRadius);
_selector->setAfterShownCallback([this](SelectorTab tab) {
if (tab == SelectorTab::Gifs) {
_controller->enableGifPauseReason(Window::GifPauseReason::SavedGifs);
_selector->setAfterShownCallback([=](SelectorTab tab) {
if (tab == SelectorTab::Gifs || tab == SelectorTab::Stickers) {
_controller->enableGifPauseReason(
Window::GifPauseReason::SavedGifs);
}
});
_selector->setBeforeHidingCallback([this](SelectorTab tab) {
if (tab == SelectorTab::Gifs) {
_controller->disableGifPauseReason(Window::GifPauseReason::SavedGifs);
_selector->setBeforeHidingCallback([=](SelectorTab tab) {
if (tab == SelectorTab::Gifs || tab == SelectorTab::Stickers) {
_controller->disableGifPauseReason(
Window::GifPauseReason::SavedGifs);
}
});
_selector->showRequests(

View File

@ -20,7 +20,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "window/window_session_controller.h" // isGifPausedAtLeastFor.
#include "data/data_session.h"
#include "data/data_document.h"
#include "lottie/lottie_animation.h"
#include "lottie/lottie_single_player.h"
#include "styles/style_history.h"
namespace {
@ -97,7 +97,7 @@ QSize HistorySticker::countCurrentSize(int newWidth) {
}
void HistorySticker::setupLottie() {
_lottie = Stickers::LottieFromDocument(
_lottie = Stickers::LottiePlayerFromDocument(
_data,
Stickers::LottieSize::MessageHistory,
QSize(st::maxStickerSize, st::maxStickerSize) * cIntRetinaFactor());

View File

@ -15,7 +15,7 @@ struct HistoryMessageReply;
struct HistoryMessageForwarded;
namespace Lottie {
class Animation;
class SinglePlayer;
} // namespace Lottie
class HistorySticker : public HistoryMedia {
@ -75,7 +75,7 @@ private:
ClickHandlerPtr _packLink;
not_null<DocumentData*> _data;
QString _emoji;
std::unique_ptr<Lottie::Animation> _lottie;
std::unique_ptr<Lottie::SinglePlayer> _lottie;
rpl::lifetime _lifetime;
};

View File

@ -932,7 +932,8 @@ Widget::~Widget() = default;
void Widget::hideFinished() {
hide();
_controller->disableGifPauseReason(Window::GifPauseReason::InlineResults);
_controller->disableGifPauseReason(
Window::GifPauseReason::InlineResults);
_inner->hideFinish(true);
_a_show.stop();
@ -953,7 +954,8 @@ void Widget::showStarted() {
recountContentMaxHeight();
_inner->preloadImages();
show();
_controller->enableGifPauseReason(Window::GifPauseReason::InlineResults);
_controller->enableGifPauseReason(
Window::GifPauseReason::InlineResults);
startShowAnimation();
} else if (_hiding) {
startOpacityAnimation(false);

View File

@ -9,6 +9,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "lottie/lottie_frame_renderer.h"
#include "lottie/lottie_cache.h"
#include "lottie/lottie_player.h"
#include "base/algorithm.h"
#include "zlib.h"
#include "logs.h"
@ -56,17 +57,6 @@ std::string UnpackGzip(const QByteArray &bytes) {
return result;
}
QByteArray ReadFile(const QString &filepath) {
auto f = QFile(filepath);
return (f.size() <= kMaxFileSize && f.open(QIODevice::ReadOnly))
? f.readAll()
: QByteArray();
}
QByteArray ReadContent(const QByteArray &data, const QString &filepath) {
return data.isEmpty() ? ReadFile(filepath) : base::duplicate(data);
}
std::optional<Error> ContentError(const QByteArray &content) {
if (content.size() > kMaxFileSize) {
qWarning() << "Lottie Error: Too large file: " << content.size();
@ -140,26 +130,6 @@ std::unique_ptr<rlottie::Animation> CreateFromContent(
} // namespace details
std::unique_ptr<Animation> FromContent(
const QByteArray &data,
const QString &filepath,
const FrameRequest &request) {
return std::make_unique<Animation>(ReadContent(data, filepath), request);
}
std::unique_ptr<Animation> FromCached(
FnMut<void(FnMut<void(QByteArray &&cached)>)> get, // Main thread.
FnMut<void(QByteArray &&cached)> put, // Unknown thread.
const QByteArray &data,
const QString &filepath,
const FrameRequest &request) {
return std::make_unique<Animation>(
std::move(get),
std::move(put),
ReadContent(data, filepath),
request);
}
QImage ReadThumbnail(const QByteArray &content) {
return Init(content, FrameRequest()).match([](
const std::unique_ptr<SharedState> &state) {
@ -169,8 +139,11 @@ QImage ReadThumbnail(const QByteArray &content) {
});
}
Animation::Animation(const QByteArray &content, const FrameRequest &request)
: _timer([=] { checkNextFrameRender(); }) {
Animation::Animation(
not_null<Player*> player,
const QByteArray &content,
const FrameRequest &request)
: _player(player) {
const auto weak = base::make_weak(this);
crl::async([=] {
crl::on_main(weak, [=, data = Init(content, request)]() mutable {
@ -180,11 +153,12 @@ Animation::Animation(const QByteArray &content, const FrameRequest &request)
}
Animation::Animation(
not_null<Player*> player,
FnMut<void(FnMut<void(QByteArray &&cached)>)> get, // Main thread.
FnMut<void(QByteArray &&cached)> put, // Unknown thread.
const QByteArray &content,
const FrameRequest &request)
: _timer([=] { checkNextFrameRender(); }) {
: _player(player) {
const auto weak = base::make_weak(this);
get([=, put = std::move(put)](QByteArray &&cached) mutable {
crl::async([=, put = std::move(put)]() mutable {
@ -196,11 +170,8 @@ Animation::Animation(
});
}
Animation::~Animation() {
if (_renderer) {
Assert(_state != nullptr);
_renderer->remove(_state);
}
bool Animation::ready() const {
return (_state != nullptr);
}
void Animation::initDone(details::InitData &&data) {
@ -214,97 +185,24 @@ void Animation::initDone(details::InitData &&data) {
void Animation::parseDone(std::unique_ptr<SharedState> state) {
Expects(state != nullptr);
auto information = state->information();
_state = state.get();
_state->start(this, crl::now());
_renderer = FrameRenderer::Instance();
_renderer->append(std::move(state));
_updates.fire({ std::move(information) });
crl::on_main_update_requests(
) | rpl::start_with_next([=] {
checkStep();
}, _lifetime);
_player->start(this, std::move(state));
}
void Animation::parseFailed(Error error) {
_updates.fire_error(std::move(error));
_player->failed(this, error);
}
QImage Animation::frame(const FrameRequest &request) const {
Expects(_renderer != nullptr);
Expects(_state != nullptr);
const auto frame = _state->frameForPaint();
const auto changed = (frame->request != request);
if (changed) {
frame->request = request;
_renderer->updateFrameRequest(_state, request);
_player->updateFrameRequest(this, request);
}
return PrepareFrameByRequest(frame, !changed);
}
rpl::producer<Update, Error> Animation::updates() const {
return _updates.events();
}
bool Animation::ready() const {
return (_renderer != nullptr);
}
crl::time Animation::markFrameDisplayed(crl::time now) {
Expects(_renderer != nullptr);
const auto result = _state->markFrameDisplayed(now);
return result;
}
crl::time Animation::markFrameShown() {
Expects(_renderer != nullptr);
const auto result = _state->markFrameShown();
_renderer->frameShown(_state);
return result;
}
void Animation::checkStep() {
if (_nextFrameTime != kTimeUnknown) {
checkNextFrameRender();
} else {
checkNextFrameAvailability();
}
}
void Animation::checkNextFrameAvailability() {
Expects(_renderer != nullptr);
_nextFrameTime = _state->nextFrameDisplayTime();
if (_nextFrameTime != kTimeUnknown) {
checkStep();
}
}
void Animation::checkNextFrameRender() {
Expects(_nextFrameTime != kTimeUnknown);
const auto now = crl::now();
if (now < _nextFrameTime) {
if (!_timer.isActive()) {
_timer.callOnce(_nextFrameTime - now);
}
} else {
_timer.cancel();
_nextFrameTime = kTimeUnknown;
const auto position = markFrameDisplayed(now);
_updates.fire({ DisplayFrameRequest{ position } });
}
}
//void Animation::play(const PlaybackOptions &options) {
// _options = options;
// _started = crl::now();
//}
} // namespace Lottie

View File

@ -7,11 +7,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#pragma once
#include "base/basic_types.h"
#include "base/flat_map.h"
#include "base/weak_ptr.h"
#include "base/timer.h"
#include "lottie/lottie_common.h"
#include "base/weak_ptr.h"
#include <QSize>
#include <crl/crl_time.h>
@ -29,23 +26,10 @@ class Animation;
namespace Lottie {
inline constexpr auto kMaxFileSize = 1024 * 1024;
class Player;
class SharedState;
class Animation;
class FrameRenderer;
std::unique_ptr<Animation> FromContent(
const QByteArray &data,
const QString &filepath,
const FrameRequest &request);
std::unique_ptr<Animation> FromCached(
FnMut<void(FnMut<void(QByteArray &&cached)>)> get, // Main thread.
FnMut<void(QByteArray &&cached)> put, // Unknown thread.
const QByteArray &data,
const QString &filepath,
const FrameRequest &request);
QImage ReadThumbnail(const QByteArray &content);
namespace details {
@ -59,47 +43,27 @@ std::unique_ptr<rlottie::Animation> CreateFromContent(
class Animation final : public base::has_weak_ptr {
public:
explicit Animation(
Animation(
not_null<Player*> player,
const QByteArray &content,
const FrameRequest &request);
Animation(
not_null<Player*> player,
FnMut<void(FnMut<void(QByteArray &&cached)>)> get, // Main thread.
FnMut<void(QByteArray &&cached)> put, // Unknown thread.
const QByteArray &content,
const FrameRequest &request);
~Animation();
//void play(const PlaybackOptions &options);
[[nodiscard]] QImage frame(const FrameRequest &request) const;
[[nodiscard]] rpl::producer<Update, Error> updates() const;
[[nodiscard]] bool ready() const;
// Returns frame position, if any frame was marked as displayed.
crl::time markFrameDisplayed(crl::time now);
crl::time markFrameShown();
void checkStep();
[[nodiscard]] QImage frame(const FrameRequest &request) const;
private:
void initDone(details::InitData &&data);
void parseDone(std::unique_ptr<SharedState> state);
void parseFailed(Error error);
void checkNextFrameAvailability();
void checkNextFrameRender();
//crl::time _started = 0;
//PlaybackOptions _options;
base::Timer _timer;
crl::time _nextFrameTime = kTimeUnknown;
not_null<Player*> _player;
SharedState *_state = nullptr;
std::shared_ptr<FrameRenderer> _renderer;
rpl::event_stream<Update, Error> _updates;
rpl::lifetime _lifetime;
};

View File

@ -0,0 +1,30 @@
/*
This file is part of Telegram Desktop,
the official desktop application for the Telegram messaging service.
For license and copyright information please follow this link:
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#include "lottie/lottie_common.h"
#include "base/algorithm.h"
#include <QFile>
namespace Lottie {
namespace {
QByteArray ReadFile(const QString &filepath) {
auto f = QFile(filepath);
return (f.size() <= kMaxFileSize && f.open(QIODevice::ReadOnly))
? f.readAll()
: QByteArray();
}
} // namespace
QByteArray ReadContent(const QByteArray &data, const QString &filepath) {
return data.isEmpty() ? ReadFile(filepath) : base::duplicate(data);
}
} // namespace Lottie

View File

@ -16,15 +16,11 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
namespace Lottie {
constexpr auto kTimeUnknown = std::numeric_limits<crl::time>::min();
inline constexpr auto kTimeUnknown = std::numeric_limits<crl::time>::min();
inline constexpr auto kMaxFileSize = 1024 * 1024;
class Animation;
struct PlaybackOptions {
float64 speed = 1.;
bool loop = true;
};
struct Information {
int frameRate = 0;
int framesCount = 0;
@ -73,4 +69,6 @@ struct FrameRequest {
}
};
QByteArray ReadContent(const QByteArray &data, const QString &filepath);
} // namespace Lottie

View File

@ -7,6 +7,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#include "lottie/lottie_frame_renderer.h"
#include "lottie/lottie_player.h"
#include "lottie/lottie_animation.h"
#include "lottie/lottie_cache.h"
#include "logs.h"
@ -290,7 +291,7 @@ void SharedState::init(QImage cover, const FrameRequest &request) {
_counter.store(0, std::memory_order_release);
}
void SharedState::start(not_null<Animation*> owner, crl::time now) {
void SharedState::start(not_null<Player*> owner, crl::time now) {
_owner = owner;
_started = now;
}

View File

@ -27,7 +27,7 @@ inline constexpr auto kMaxFrameRate = 120;
inline constexpr auto kMaxSize = 3096;
inline constexpr auto kMaxFramesCount = 600;
class Animation;
class Player;
class Cache;
struct Frame {
@ -55,7 +55,7 @@ public:
std::unique_ptr<Cache> cache,
const FrameRequest &request);
void start(not_null<Animation*> owner, crl::time now);
void start(not_null<Player*> owner, crl::time now);
[[nodiscard]] Information information() const;
[[nodiscard]] bool initialized() const;
@ -91,7 +91,7 @@ private:
static constexpr auto kFramesCount = 4;
std::array<Frame, kFramesCount> _frames;
base::weak_ptr<Animation> _owner;
base::weak_ptr<Player> _owner;
crl::time _started = kTimeUnknown;
crl::time _duration = kTimeUnknown;
int _frameIndex = 0;
@ -111,6 +111,7 @@ public:
static std::shared_ptr<FrameRenderer> Instance();
void append(std::unique_ptr<SharedState> entry);
void updateFrameRequest(
not_null<SharedState*> entry,
const FrameRequest &request);

View File

@ -0,0 +1,42 @@
/*
This file is part of Telegram Desktop,
the official desktop application for the Telegram messaging service.
For license and copyright information please follow this link:
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#pragma once
#include "lottie/lottie_common.h"
#include "base/weak_ptr.h"
#include <rpl/producer.h>
namespace Lottie {
class SharedState;
class Player : public base::has_weak_ptr {
public:
virtual void start(
not_null<Animation*> animation,
std::unique_ptr<SharedState> state) = 0;
virtual void failed(not_null<Animation*> animation, Error error) = 0;
[[nodiscard]] virtual rpl::producer<Update, Error> updates() = 0;
virtual void updateFrameRequest(
not_null<const Animation*> animation,
const FrameRequest &request) = 0;
// Returns frame position, if any frame was marked as displayed.
virtual crl::time markFrameDisplayed(crl::time now) = 0;
virtual crl::time markFrameShown() = 0;
virtual void checkStep() = 0;
virtual ~Player() = default;
};
} // namespace Lottie

View File

@ -0,0 +1,135 @@
/*
This file is part of Telegram Desktop,
the official desktop application for the Telegram messaging service.
For license and copyright information please follow this link:
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#include "lottie/lottie_single_player.h"
#include "lottie/lottie_frame_renderer.h"
namespace Lottie {
namespace {
} // namespace
SinglePlayer::SinglePlayer(
const QByteArray &content,
const FrameRequest &request)
: _animation(this, content, request)
, _timer([=] { checkNextFrameRender(); })
, _renderer(FrameRenderer::Instance()) {
}
SinglePlayer::SinglePlayer(
FnMut<void(FnMut<void(QByteArray &&cached)>)> get, // Main thread.
FnMut<void(QByteArray &&cached)> put, // Unknown thread.
const QByteArray &content,
const FrameRequest &request)
: _animation(this, std::move(get), std::move(put), content, request)
, _timer([=] { checkNextFrameRender(); })
, _renderer(FrameRenderer::Instance()) {
}
void SinglePlayer::start(
not_null<Animation*> animation,
std::unique_ptr<SharedState> state) {
Expects(animation == &_animation);
_state = state.get();
auto information = state->information();
state->start(this, crl::now());
_renderer = FrameRenderer::Instance();
_renderer->append(std::move(state));
_updates.fire({ std::move(information) });
crl::on_main_update_requests(
) | rpl::start_with_next([=] {
checkStep();
}, _lifetime);
}
void SinglePlayer::failed(not_null<Animation*> animation, Error error) {
Expects(animation == &_animation);
_updates.fire_error(std::move(error));
}
SinglePlayer::~SinglePlayer() {
if (_state) {
_renderer->remove(_state);
}
}
rpl::producer<Update, Error> SinglePlayer::updates() {
return _updates.events();
}
bool SinglePlayer::ready() const {
return _animation.ready();
}
QImage SinglePlayer::frame(const FrameRequest &request) const {
return _animation.frame(request);
}
void SinglePlayer::checkStep() {
if (_nextFrameTime != kTimeUnknown) {
checkNextFrameRender();
} else {
checkNextFrameAvailability();
}
}
void SinglePlayer::checkNextFrameAvailability() {
Expects(_state != nullptr);
_nextFrameTime = _state->nextFrameDisplayTime();
if (_nextFrameTime != kTimeUnknown) {
checkStep();
}
}
void SinglePlayer::checkNextFrameRender() {
Expects(_nextFrameTime != kTimeUnknown);
const auto now = crl::now();
if (now < _nextFrameTime) {
if (!_timer.isActive()) {
_timer.callOnce(_nextFrameTime - now);
}
} else {
_timer.cancel();
_nextFrameTime = kTimeUnknown;
const auto position = markFrameDisplayed(now);
_updates.fire({ DisplayFrameRequest{ position } });
}
}
void SinglePlayer::updateFrameRequest(
not_null<const Animation*> animation,
const FrameRequest &request) {
Expects(animation == &_animation);
Expects(_state != nullptr);
_renderer->updateFrameRequest(_state, request);
}
crl::time SinglePlayer::markFrameDisplayed(crl::time now) {
Expects(_state != nullptr);
return _state->markFrameDisplayed(now);
}
crl::time SinglePlayer::markFrameShown() {
Expects(_renderer != nullptr);
const auto result = _state->markFrameShown();
_renderer->frameShown(_state);
return result;
}
} // namespace Lottie

View File

@ -0,0 +1,62 @@
/*
This file is part of Telegram Desktop,
the official desktop application for the Telegram messaging service.
For license and copyright information please follow this link:
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#pragma once
#include "lottie/lottie_player.h"
#include "lottie/lottie_animation.h"
#include "base/timer.h"
namespace Lottie {
class SinglePlayer final : public Player {
public:
SinglePlayer(
const QByteArray &content,
const FrameRequest &request);
SinglePlayer(
FnMut<void(FnMut<void(QByteArray &&cached)>)> get, // Main thread.
FnMut<void(QByteArray &&cached)> put, // Unknown thread.
const QByteArray &content,
const FrameRequest &request);
~SinglePlayer();
void start(
not_null<Animation*> animation,
std::unique_ptr<SharedState> state) override;
void failed(not_null<Animation*> animation, Error error) override;
rpl::producer<Update, Error> updates() override;
[[nodiscard]] bool ready() const;
[[nodiscard]] QImage frame(const FrameRequest &request) const;
void updateFrameRequest(
not_null<const Animation*> animation,
const FrameRequest &request) override;
// Returns frame position, if any frame was marked as displayed.
crl::time markFrameDisplayed(crl::time now) override;
crl::time markFrameShown() override;
void checkStep() override;
private:
void checkNextFrameAvailability();
void checkNextFrameRender();
Animation _animation;
base::Timer _timer;
std::shared_ptr<FrameRenderer> _renderer;
SharedState *_state = nullptr;
crl::time _nextFrameTime = kTimeUnknown;
rpl::event_stream<Update, Error> _updates;
rpl::lifetime _lifetime;
};
} // namespace Lottie

View File

@ -27,7 +27,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "media/streaming/media_streaming_player.h"
#include "media/streaming/media_streaming_reader.h"
#include "media/player/media_player_instance.h"
#include "lottie/lottie_animation.h"
#include "history/history.h"
#include "history/history_message.h"
#include "data/data_media_types.h"
@ -43,7 +42,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "auth_session.h"
#include "layout.h"
#include "storage/file_download.h"
#include "lottie/lottie_animation.h"
#include "calls/calls_instance.h"
#include "styles/style_mediaview.h"
#include "styles/style_history.h"
@ -195,12 +193,6 @@ struct OverlayWidget::Streamed {
bool resumeOnCallEnd = false;
};
struct OverlayWidget::LottieFile {
LottieFile(std::unique_ptr<Lottie::Animation> data);
std::unique_ptr<Lottie::Animation> data;
};
template <typename Callback>
OverlayWidget::Streamed::Streamed(
not_null<Data::Session*> owner,
@ -215,11 +207,6 @@ OverlayWidget::Streamed::Streamed(
st::mediaviewStreamingRadial) {
}
OverlayWidget::LottieFile::LottieFile(
std::unique_ptr<Lottie::Animation> data)
: data(std::move(data)) {
}
OverlayWidget::OverlayWidget()
: OverlayParent(nullptr)
, _transparentBrush(style::transparentPlaceholderBrush())
@ -438,7 +425,6 @@ bool OverlayWidget::documentBubbleShown() const {
|| (_doc
&& !_themePreviewShown
&& !_streamed
&& !_lottie
&& _current.isNull());
}
@ -447,10 +433,6 @@ void OverlayWidget::clearStreaming() {
_streamed = nullptr;
}
void OverlayWidget::clearLottie() {
_lottie = nullptr;
}
void OverlayWidget::documentUpdated(DocumentData *doc) {
if (_doc && _doc == doc) {
if (documentBubbleShown()) {
@ -980,7 +962,6 @@ void OverlayWidget::clearData() {
_animationOpacities.clear();
}
clearStreaming();
clearLottie();
delete _menu;
_menu = nullptr;
setContext(std::nullopt);
@ -1776,7 +1757,6 @@ void OverlayWidget::displayPhoto(not_null<PhotoData*> photo, HistoryItem *item)
}
clearStreaming();
clearLottie();
destroyThemePreview();
_doc = nullptr;
_fullScreenVideo = false;
@ -1828,7 +1808,6 @@ void OverlayWidget::displayDocument(DocumentData *doc, HistoryItem *item) {
_fullScreenVideo = false;
_current = QPixmap();
clearStreaming();
clearLottie();
destroyThemePreview();
_doc = doc;
_photo = nullptr;
@ -1861,13 +1840,6 @@ void OverlayWidget::displayDocument(DocumentData *doc, HistoryItem *item) {
const auto &path = location.name();
if (QImageReader(path).canRead()) {
_current = PrepareStaticImage(path);
//} else if (auto lottie = Lottie::FromFile(path)) {
// _lottie = std::make_unique<LottieFile>(
// std::move(lottie));
// _lottie->data->updates(
// ) | rpl::start_with_next([=] {
// update();
// }, lifetime());
}
}
location.accessDisable();
@ -2561,8 +2533,6 @@ void OverlayWidget::paintEvent(QPaintEvent *e) {
}
} else if (_themePreviewShown) {
paintThemePreview(p, r);
} else if (_lottie) {
paintLottieFrame(p, r);
} else if (documentBubbleShown()) {
if (_docRect.intersects(r)) {
p.fillRect(_docRect, st::mediaviewFileBg);
@ -2929,20 +2899,6 @@ void OverlayWidget::paintThemePreview(Painter &p, QRect clip) {
}
}
void OverlayWidget::paintLottieFrame(Painter &p, QRect clip) {
Expects(_lottie != nullptr);
if (_lottie->data->ready()) {
_lottie->data->markFrameShown();
const auto frame = _lottie->data->frame(Lottie::FrameRequest());
const auto x = (width() - frame.width()) / 2;
const auto y = (height() - frame.height()) / 2;
const auto background = _lottieDark ? Qt::black : Qt::white;
p.fillRect(x, y, frame.width(), frame.height(), background);
p.drawImage(x, y, frame);
}
}
void OverlayWidget::keyPressEvent(QKeyEvent *e) {
const auto ctrl = e->modifiers().testFlag(Qt::ControlModifier);
if (_streamed) {
@ -2996,7 +2952,6 @@ void OverlayWidget::keyPressEvent(QKeyEvent *e) {
} else if (e->key() == Qt::Key_0) {
zoomReset();
} else if (e->key() == Qt::Key_I) {
_lottieDark = !_lottieDark;
update();
}
}
@ -3191,7 +3146,6 @@ bool OverlayWidget::moveToEntity(const Entity &entity, int preloadDelta) {
setContext(std::nullopt);
}
clearStreaming();
clearLottie();
_streamingStartPaused = false;
if (auto photo = base::get_if<not_null<PhotoData*>>(&entity.data)) {
displayPhoto(*photo, entity.item);
@ -3686,7 +3640,6 @@ void OverlayWidget::setVisibleHook(bool visible) {
QCoreApplication::instance()->removeEventFilter(this);
clearStreaming();
clearLottie();
destroyThemePreview();
_radial.stop();
_current = QPixmap();

View File

@ -120,7 +120,6 @@ private slots:
private:
struct Streamed;
struct LottieFile;
enum OverState {
OverNone,
@ -313,9 +312,6 @@ private:
void paintTransformedVideoFrame(Painter &p);
void clearStreaming();
void paintLottieFrame(Painter &p, QRect clip);
void clearLottie();
QBrush _transparentBrush;
PhotoData *_photo = nullptr;
@ -364,8 +360,6 @@ private:
bool _blurred = true;
std::unique_ptr<Streamed> _streamed;
std::unique_ptr<LottieFile> _lottie;
bool _lottieDark = false;
const style::icon *_docIcon = nullptr;
style::color _docIconColor;

View File

@ -19,7 +19,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "ui/image/image.h"
#include "ui/emoji_config.h"
#include "window/window_main_menu.h"
#include "lottie/lottie_animation.h"
#include "lottie/lottie_single_player.h"
#include "auth_session.h"
#include "chat_helpers/stickers.h"
#include "window/window_session_controller.h"
@ -1049,9 +1049,8 @@ QSize MediaPreviewWidget::currentDimensions() const {
void MediaPreviewWidget::setupLottie() {
Expects(_document != nullptr);
_lottie = Lottie::FromContent(
_document->data(),
_document->filepath(),
_lottie = std::make_unique<Lottie::SinglePlayer>(
Lottie::ReadContent(_document->data(), _document->filepath()),
Lottie::FrameRequest{ currentDimensions() * cIntRetinaFactor() });
_lottie->updates(

View File

@ -12,7 +12,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "data/data_file_origin.h"
namespace Lottie {
class Animation;
class SinglePlayer;
} // namespace Lottie
namespace Window {
@ -236,7 +236,7 @@ private:
DocumentData *_document = nullptr;
PhotoData *_photo = nullptr;
Media::Clip::ReaderPointer _gif;
std::unique_ptr<Lottie::Animation> _lottie;
std::unique_ptr<Lottie::SinglePlayer> _lottie;
int _emojiSize;
std::vector<not_null<EmojiPtr>> _emojiList;

View File

@ -60,9 +60,13 @@
'<(src_loc)/lottie/lottie_animation.h',
'<(src_loc)/lottie/lottie_cache.cpp',
'<(src_loc)/lottie/lottie_cache.h',
'<(src_loc)/lottie/lottie_common.cpp',
'<(src_loc)/lottie/lottie_common.h',
'<(src_loc)/lottie/lottie_frame_renderer.cpp',
'<(src_loc)/lottie/lottie_frame_renderer.h',
'<(src_loc)/lottie/lottie_player.h',
'<(src_loc)/lottie/lottie_single_player.cpp',
'<(src_loc)/lottie/lottie_single_player.h',
],
'conditions': [[ 'build_macold', {
'xcode_settings': {