Use lottie instead of webp in bottom info reactions.

This commit is contained in:
John Preston 2022-01-05 15:10:17 +03:00
parent 82523978c9
commit 409a3357da
2 changed files with 49 additions and 28 deletions

View file

@ -16,6 +16,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "data/data_document.h" #include "data/data_document.h"
#include "data/data_document_media.h" #include "data/data_document_media.h"
#include "data/data_changes.h" #include "data/data_changes.h"
#include "lottie/lottie_icon.h"
#include "base/timer_rpl.h" #include "base/timer_rpl.h"
#include "apiwrap.h" #include "apiwrap.h"
#include "styles/style_chat.h" #include "styles/style_chat.h"
@ -25,6 +26,7 @@ namespace {
constexpr auto kRefreshFullListEach = 60 * 60 * crl::time(1000); constexpr auto kRefreshFullListEach = 60 * 60 * crl::time(1000);
constexpr auto kPollEach = 20 * crl::time(1000); constexpr auto kPollEach = 20 * crl::time(1000);
constexpr auto kSizeForDownscale = 128;
} // namespace } // namespace
@ -49,6 +51,8 @@ Reactions::Reactions(not_null<Session*> owner)
}, _lifetime); }, _lifetime);
} }
Reactions::~Reactions() = default;
void Reactions::refresh() { void Reactions::refresh() {
request(); request();
} }
@ -82,7 +86,7 @@ void Reactions::preloadImageFor(const QString &emoji) {
auto &set = _images.emplace(emoji).first->second; auto &set = _images.emplace(emoji).first->second;
const auto i = ranges::find(_available, emoji, &Reaction::emoji); const auto i = ranges::find(_available, emoji, &Reaction::emoji);
const auto document = (i != end(_available)) const auto document = (i != end(_available))
? i->staticIcon.get() ? i->centerIcon.get()
: nullptr; : nullptr;
if (document) { if (document) {
loadImage(set, document); loadImage(set, document);
@ -100,6 +104,20 @@ QImage Reactions::resolveImageFor(
preloadImageFor(emoji); preloadImageFor(emoji);
} }
auto &set = (i != end(_images)) ? i->second : _images[emoji]; auto &set = (i != end(_images)) ? i->second : _images[emoji];
const auto resolve = [&](QImage &image, int size) {
const auto factor = style::DevicePixelRatio();
image = set.icon->frame().scaled(
size * factor,
size * factor,
Qt::IgnoreAspectRatio,
Qt::SmoothTransformation);
image.setDevicePixelRatio(factor);
};
if (set.bottomInfo.isNull() && set.icon) {
resolve(set.bottomInfo, st::reactionInfoSize);
resolve(set.inlineList, st::reactionBottomSize);
crl::async([icon = std::move(set.icon)]{});
}
switch (size) { switch (size) {
case ImageSize::BottomInfo: return set.bottomInfo; case ImageSize::BottomInfo: return set.bottomInfo;
case ImageSize::InlineList: return set.inlineList; case ImageSize::InlineList: return set.inlineList;
@ -109,12 +127,12 @@ QImage Reactions::resolveImageFor(
void Reactions::resolveImages() { void Reactions::resolveImages() {
for (auto &[emoji, set] : _images) { for (auto &[emoji, set] : _images) {
if (!set.bottomInfo.isNull() || set.media) { if (!set.bottomInfo.isNull() || set.icon || set.media) {
continue; continue;
} }
const auto i = ranges::find(_available, emoji, &Reaction::emoji); const auto i = ranges::find(_available, emoji, &Reaction::emoji);
const auto document = (i != end(_available)) const auto document = (i != end(_available))
? i->staticIcon.get() ? i->centerIcon.get()
: nullptr; : nullptr;
if (document) { if (document) {
loadImage(set, document); loadImage(set, document);
@ -128,13 +146,14 @@ void Reactions::resolveImages() {
void Reactions::loadImage( void Reactions::loadImage(
ImageSet &set, ImageSet &set,
not_null<DocumentData*> document) { not_null<DocumentData*> document) {
if (!set.bottomInfo.isNull()) { if (!set.bottomInfo.isNull() || set.icon) {
return; return;
} else if (!set.media) { } else if (!set.media) {
set.media = document->createMediaView(); set.media = document->createMediaView();
set.media->checkStickerLarge();
} }
if (const auto image = set.media->getStickerLarge()) { if (set.media->loaded()) {
setImage(set, image->original()); setLottie(set);
} else if (!_imagesLoadLifetime) { } else if (!_imagesLoadLifetime) {
document->session().downloaderTaskFinished( document->session().downloaderTaskFinished(
) | rpl::start_with_next([=] { ) | rpl::start_with_next([=] {
@ -143,20 +162,15 @@ void Reactions::loadImage(
} }
} }
void Reactions::setImage(ImageSet &set, QImage large) { void Reactions::setLottie(ImageSet &set) {
const auto size = kSizeForDownscale / style::DevicePixelRatio();
set.icon = std::make_unique<Lottie::Icon>(Lottie::IconDescriptor{
.path = set.media->owner()->filepath(true),
.json = set.media->bytes(),
.sizeOverride = QSize(size, size),
.frame = -1,
});
set.media = nullptr; set.media = nullptr;
const auto scale = [&](int size) {
const auto factor = style::DevicePixelRatio();
return Images::prepare(
large,
size * factor,
size * factor,
Images::Option::Smooth,
size,
size);
};
set.bottomInfo = scale(st::reactionInfoSize);
set.inlineList = scale(st::reactionBottomSize);
} }
void Reactions::downloadTaskFinished() { void Reactions::downloadTaskFinished() {
@ -164,8 +178,8 @@ void Reactions::downloadTaskFinished() {
for (auto &[emoji, set] : _images) { for (auto &[emoji, set] : _images) {
if (!set.media) { if (!set.media) {
continue; continue;
} else if (const auto image = set.media->getStickerLarge()) { } else if (set.media->loaded()) {
setImage(set, image->original()); setLottie(set);
} else { } else {
hasOne = true; hasOne = true;
} }
@ -249,6 +263,8 @@ std::optional<Reaction> Reactions::parse(const MTPAvailableReaction &entry) {
if (!known) { if (!known) {
LOG(("API Error: Unknown emoji in reactions: %1").arg(emoji)); LOG(("API Error: Unknown emoji in reactions: %1").arg(emoji));
} }
const auto selectAnimation = _owner->processDocument(
data.vselect_animation());
return known return known
? std::make_optional(Reaction{ ? std::make_optional(Reaction{
.emoji = emoji, .emoji = emoji,
@ -256,19 +272,18 @@ std::optional<Reaction> Reactions::parse(const MTPAvailableReaction &entry) {
.staticIcon = _owner->processDocument(data.vstatic_icon()), .staticIcon = _owner->processDocument(data.vstatic_icon()),
.appearAnimation = _owner->processDocument( .appearAnimation = _owner->processDocument(
data.vappear_animation()), data.vappear_animation()),
.selectAnimation = _owner->processDocument( .selectAnimation = selectAnimation,
data.vselect_animation()),
.activateAnimation = _owner->processDocument( .activateAnimation = _owner->processDocument(
data.vactivate_animation()), data.vactivate_animation()),
.activateEffects = _owner->processDocument( .activateEffects = _owner->processDocument(
data.veffect_animation()), data.veffect_animation()),
.centerIcon = (data.vcenter_icon()
? _owner->processDocument(*data.vcenter_icon())
: selectAnimation),
.aroundAnimation = (data.varound_animation() .aroundAnimation = (data.varound_animation()
? _owner->processDocument( ? _owner->processDocument(
*data.varound_animation()).get() *data.varound_animation()).get()
: nullptr), : nullptr),
.centerIcon = (data.vcenter_icon()
? _owner->processDocument(*data.vcenter_icon()).get()
: nullptr),
.active = !data.is_inactive(), .active = !data.is_inactive(),
}) })
: std::nullopt; : std::nullopt;

View file

@ -9,6 +9,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "base/timer.h" #include "base/timer.h"
namespace Lottie {
class Icon;
} // namespace Lottie
namespace Data { namespace Data {
class DocumentMedia; class DocumentMedia;
@ -22,14 +26,15 @@ struct Reaction {
not_null<DocumentData*> selectAnimation; not_null<DocumentData*> selectAnimation;
not_null<DocumentData*> activateAnimation; not_null<DocumentData*> activateAnimation;
not_null<DocumentData*> activateEffects; not_null<DocumentData*> activateEffects;
not_null<DocumentData*> centerIcon;
DocumentData *aroundAnimation = nullptr; DocumentData *aroundAnimation = nullptr;
DocumentData *centerIcon = nullptr;
bool active = false; bool active = false;
}; };
class Reactions final { class Reactions final {
public: public:
explicit Reactions(not_null<Session*> owner); explicit Reactions(not_null<Session*> owner);
~Reactions();
void refresh(); void refresh();
@ -72,6 +77,7 @@ private:
QImage bottomInfo; QImage bottomInfo;
QImage inlineList; QImage inlineList;
std::shared_ptr<DocumentMedia> media; std::shared_ptr<DocumentMedia> media;
std::unique_ptr<Lottie::Icon> icon;
}; };
void request(); void request();
@ -80,7 +86,7 @@ private:
const MTPAvailableReaction &entry); const MTPAvailableReaction &entry);
void loadImage(ImageSet &set, not_null<DocumentData*> document); void loadImage(ImageSet &set, not_null<DocumentData*> document);
void setImage(ImageSet &set, QImage large); void setLottie(ImageSet &set);
void resolveImages(); void resolveImages();
void downloadTaskFinished(); void downloadTaskFinished();