Show premium effects in sticker preview.

This commit is contained in:
John Preston 2022-04-28 13:15:12 +04:00
parent af9a252b64
commit 877be8e6cb
2 changed files with 98 additions and 24 deletions

View file

@ -25,7 +25,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
namespace Window { namespace Window {
namespace { namespace {
constexpr int kStickerPreviewEmojiLimit = 10; constexpr auto kStickerPreviewEmojiLimit = 10;
constexpr auto kPremiumMultiplier = 2.25;
constexpr auto kPremiumDownscale = 1.5;
} // namespace } // namespace
@ -44,25 +46,34 @@ MediaPreviewWidget::MediaPreviewWidget(
QRect MediaPreviewWidget::updateArea() const { QRect MediaPreviewWidget::updateArea() const {
const auto size = currentDimensions(); const auto size = currentDimensions();
return QRect( const auto position = QPoint(
QPoint((width() - size.width()) / 2, (height() - size.height()) / 2), (width() - size.width()) / 2,
size); (height() - size.height()) / 2);
const auto premium = _document && _document->isPremiumSticker();
const auto adjusted = position
- (premium
? QPoint(size.width() - (size.width() / 2), size.height() / 2)
: QPoint());
return QRect(adjusted, size * (premium ? 2 : 1));
} }
void MediaPreviewWidget::paintEvent(QPaintEvent *e) { void MediaPreviewWidget::paintEvent(QPaintEvent *e) {
Painter p(this); Painter p(this);
QRect r(e->rect());
const auto image = [&] { const auto r = e->rect();
if (!_lottie || !_lottie->ready()) { const auto factor = cIntRetinaFactor();
return QImage(); const auto dimensions = currentDimensions();
} const auto frame = (_lottie && _lottie->ready())
_lottie->markFrameShown(); ? _lottie->frameInfo({ dimensions * factor })
return _lottie->frame(); : Lottie::Animation::FrameInfo();
}(); const auto effect = (_effect && _effect->ready())
? _effect->frameInfo({ dimensions * kPremiumMultiplier * factor })
: Lottie::Animation::FrameInfo();
const auto image = frame.image;
const auto effectImage = effect.image;
const auto pixmap = image.isNull() ? currentImage() : QPixmap(); const auto pixmap = image.isNull() ? currentImage() : QPixmap();
const auto size = image.isNull() ? pixmap.size() : image.size(); const auto size = image.isNull() ? pixmap.size() : image.size();
int w = size.width() / cIntRetinaFactor(), h = size.height() / cIntRetinaFactor(); int w = size.width() / factor, h = size.height() / factor;
auto shown = _a_shown.value(_hiding ? 0. : 1.); auto shown = _a_shown.value(_hiding ? 0. : 1.);
if (!_a_shown.animating()) { if (!_a_shown.animating()) {
if (_hiding) { if (_hiding) {
@ -76,12 +87,14 @@ void MediaPreviewWidget::paintEvent(QPaintEvent *e) {
// h = qMax(qRound(h * (st::stickerPreviewMin + ((1. - st::stickerPreviewMin) * shown)) / 2.) * 2 + int(h % 2), 1); // h = qMax(qRound(h * (st::stickerPreviewMin + ((1. - st::stickerPreviewMin) * shown)) / 2.) * 2 + int(h % 2), 1);
} }
p.fillRect(r, st::stickerPreviewBg); p.fillRect(r, st::stickerPreviewBg);
const auto position = innerPosition({ w, h });
if (image.isNull()) { if (image.isNull()) {
p.drawPixmap((width() - w) / 2, (height() - h) / 2, pixmap); p.drawPixmap(position, pixmap);
} else { } else {
p.drawImage( p.drawImage(QRect(position, QSize(w, h)), image);
QRect((width() - w) / 2, (height() - h) / 2, w, h), }
image); if (!effectImage.isNull()) {
p.drawImage(outerPosition({ w, h }), effectImage);
} }
if (!_emojiList.empty()) { if (!_emojiList.empty()) {
const auto emojiCount = _emojiList.size(); const auto emojiCount = _emojiList.size();
@ -98,12 +111,39 @@ void MediaPreviewWidget::paintEvent(QPaintEvent *e) {
emojiLeft += _emojiSize + st::stickerEmojiSkip; emojiLeft += _emojiSize + st::stickerEmojiSkip;
} }
} }
if (!frame.image.isNull() && frame.index <= effect.index) {
_lottie->markFrameShown();
}
if (!effect.image.isNull() && effect.index <= frame.index) {
_effect->markFrameShown();
}
} }
void MediaPreviewWidget::resizeEvent(QResizeEvent *e) { void MediaPreviewWidget::resizeEvent(QResizeEvent *e) {
update(); update();
} }
QPoint MediaPreviewWidget::innerPosition(QSize size) const {
if (!_document || !_document->isPremiumSticker()) {
return QPoint(
(width() - size.width()) / 2,
(height() - size.height()) / 2);
}
const auto outer = size * kPremiumMultiplier;
const auto shift = outer.width() / 40;
return outerPosition(size)
+ QPoint(
outer.width() - size.width() - shift,
(outer.height() - size.height()) / 2);
}
QPoint MediaPreviewWidget::outerPosition(QSize size) const {
const auto outer = size * kPremiumMultiplier;
return QPoint(
(width() - outer.width()) / 2,
(height() - outer.height()) / 2);
}
void MediaPreviewWidget::showPreview( void MediaPreviewWidget::showPreview(
Data::FileOrigin origin, Data::FileOrigin origin,
not_null<DocumentData*> document) { not_null<DocumentData*> document) {
@ -189,6 +229,7 @@ void MediaPreviewWidget::fillEmojiString() {
void MediaPreviewWidget::resetGifAndCache() { void MediaPreviewWidget::resetGifAndCache() {
_lottie = nullptr; _lottie = nullptr;
_effect = nullptr;
_gif.reset(); _gif.reset();
_gifThumbnail.reset(); _gifThumbnail.reset();
_gifLastPosition = 0; _gifLastPosition = 0;
@ -220,6 +261,9 @@ QSize MediaPreviewWidget::currentDimensions() const {
} }
if (_document->sticker()) { if (_document->sticker()) {
box = QSize(st::maxStickerSize, st::maxStickerSize); box = QSize(st::maxStickerSize, st::maxStickerSize);
if (_document->isPremiumSticker()) {
result = (box /= kPremiumDownscale);
}
} else { } else {
box = QSize(2 * st::maxStickerSize, 2 * st::maxStickerSize); box = QSize(2 * st::maxStickerSize, 2 * st::maxStickerSize);
} }
@ -239,22 +283,49 @@ QSize MediaPreviewWidget::currentDimensions() const {
return result; return result;
} }
void MediaPreviewWidget::createLottieIfReady(
not_null<DocumentData*> document) {
const auto sticker = document->sticker();
if (!sticker
|| !sticker->isLottie()
|| _lottie
|| !_documentMedia->loaded()) {
return;
} else if (document->isPremiumSticker()
&& _documentMedia->videoThumbnailContent().isEmpty()) {
return;
}
const_cast<MediaPreviewWidget*>(this)->setupLottie();
}
void MediaPreviewWidget::setupLottie() { void MediaPreviewWidget::setupLottie() {
Expects(_document != nullptr); Expects(_document != nullptr);
const auto factor = cIntRetinaFactor();
const auto size = currentDimensions();
_lottie = std::make_unique<Lottie::SinglePlayer>( _lottie = std::make_unique<Lottie::SinglePlayer>(
Lottie::ReadContent(_documentMedia->bytes(), _document->filepath()), Lottie::ReadContent(_documentMedia->bytes(), _document->filepath()),
Lottie::FrameRequest{ currentDimensions() * cIntRetinaFactor() }, Lottie::FrameRequest{ size * factor },
Lottie::Quality::High); Lottie::Quality::High);
if (_document->isPremiumSticker()) {
_effect = std::make_unique<Lottie::SinglePlayer>(
Lottie::ReadContent(_documentMedia->videoThumbnailContent(), {}),
Lottie::FrameRequest{ size * kPremiumMultiplier * factor },
Lottie::Quality::High);
}
_lottie->updates( const auto handler = [=](Lottie::Update update) {
) | rpl::start_with_next([=](Lottie::Update update) {
v::match(update.data, [&](const Lottie::Information &) { v::match(update.data, [&](const Lottie::Information &) {
this->update(); this->update();
}, [&](const Lottie::DisplayFrameRequest &) { }, [&](const Lottie::DisplayFrameRequest &) {
this->update(updateArea()); this->update(updateArea());
}); });
}, lifetime()); };
_lottie->updates() | rpl::start_with_next(handler, lifetime());
if (_effect) {
_effect->updates() | rpl::start_with_next(handler, lifetime());
}
} }
QPixmap MediaPreviewWidget::currentImage() const { QPixmap MediaPreviewWidget::currentImage() const {
@ -264,9 +335,8 @@ QPixmap MediaPreviewWidget::currentImage() const {
const auto webm = sticker && sticker->isWebm(); const auto webm = sticker && sticker->isWebm();
if (sticker && !webm) { if (sticker && !webm) {
if (_cacheStatus != CacheLoaded) { if (_cacheStatus != CacheLoaded) {
if (sticker->isLottie() && !_lottie && _documentMedia->loaded()) { const_cast<MediaPreviewWidget*>(this)->createLottieIfReady(
const_cast<MediaPreviewWidget*>(this)->setupLottie(); _document);
}
if (_lottie && _lottie->ready()) { if (_lottie && _lottie->ready()) {
return QPixmap(); return QPixmap();
} else if (const auto image = _documentMedia->getStickerLarge()) { } else if (const auto image = _documentMedia->getStickerLarge()) {

View file

@ -50,10 +50,13 @@ private:
void startGifAnimation(const Media::Clip::ReaderPointer &gif); void startGifAnimation(const Media::Clip::ReaderPointer &gif);
QSize currentDimensions() const; QSize currentDimensions() const;
QPixmap currentImage() const; QPixmap currentImage() const;
void createLottieIfReady(not_null<DocumentData*> document);
void setupLottie(); void setupLottie();
void startShow(); void startShow();
void fillEmojiString(); void fillEmojiString();
void resetGifAndCache(); void resetGifAndCache();
[[nodiscard]] QPoint innerPosition(QSize size) const;
[[nodiscard]] QPoint outerPosition(QSize size) const;
[[nodiscard]] QRect updateArea() const; [[nodiscard]] QRect updateArea() const;
not_null<Window::SessionController*> _controller; not_null<Window::SessionController*> _controller;
@ -69,6 +72,7 @@ private:
bool _gifWithAlpha = false; bool _gifWithAlpha = false;
crl::time _gifLastPosition = 0; crl::time _gifLastPosition = 0;
std::unique_ptr<Lottie::SinglePlayer> _lottie; std::unique_ptr<Lottie::SinglePlayer> _lottie;
std::unique_ptr<Lottie::SinglePlayer> _effect;
int _emojiSize; int _emojiSize;
std::vector<not_null<EmojiPtr>> _emojiList; std::vector<not_null<EmojiPtr>> _emojiList;