diff --git a/Telegram/SourceFiles/window/window_media_preview.cpp b/Telegram/SourceFiles/window/window_media_preview.cpp index bb700e01b7..594a36d2a5 100644 --- a/Telegram/SourceFiles/window/window_media_preview.cpp +++ b/Telegram/SourceFiles/window/window_media_preview.cpp @@ -25,7 +25,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL namespace Window { namespace { -constexpr int kStickerPreviewEmojiLimit = 10; +constexpr auto kStickerPreviewEmojiLimit = 10; +constexpr auto kPremiumMultiplier = 2.25; +constexpr auto kPremiumDownscale = 1.5; } // namespace @@ -44,25 +46,34 @@ MediaPreviewWidget::MediaPreviewWidget( QRect MediaPreviewWidget::updateArea() const { const auto size = currentDimensions(); - return QRect( - QPoint((width() - size.width()) / 2, (height() - size.height()) / 2), - size); + const auto position = QPoint( + (width() - size.width()) / 2, + (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) { Painter p(this); - QRect r(e->rect()); - const auto image = [&] { - if (!_lottie || !_lottie->ready()) { - return QImage(); - } - _lottie->markFrameShown(); - return _lottie->frame(); - }(); + const auto r = e->rect(); + const auto factor = cIntRetinaFactor(); + const auto dimensions = currentDimensions(); + const auto frame = (_lottie && _lottie->ready()) + ? _lottie->frameInfo({ dimensions * factor }) + : 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 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.); if (!_a_shown.animating()) { 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); } p.fillRect(r, st::stickerPreviewBg); + const auto position = innerPosition({ w, h }); if (image.isNull()) { - p.drawPixmap((width() - w) / 2, (height() - h) / 2, pixmap); + p.drawPixmap(position, pixmap); } else { - p.drawImage( - QRect((width() - w) / 2, (height() - h) / 2, w, h), - image); + p.drawImage(QRect(position, QSize(w, h)), image); + } + if (!effectImage.isNull()) { + p.drawImage(outerPosition({ w, h }), effectImage); } if (!_emojiList.empty()) { const auto emojiCount = _emojiList.size(); @@ -98,12 +111,39 @@ void MediaPreviewWidget::paintEvent(QPaintEvent *e) { 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) { 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( Data::FileOrigin origin, not_null document) { @@ -189,6 +229,7 @@ void MediaPreviewWidget::fillEmojiString() { void MediaPreviewWidget::resetGifAndCache() { _lottie = nullptr; + _effect = nullptr; _gif.reset(); _gifThumbnail.reset(); _gifLastPosition = 0; @@ -220,6 +261,9 @@ QSize MediaPreviewWidget::currentDimensions() const { } if (_document->sticker()) { box = QSize(st::maxStickerSize, st::maxStickerSize); + if (_document->isPremiumSticker()) { + result = (box /= kPremiumDownscale); + } } else { box = QSize(2 * st::maxStickerSize, 2 * st::maxStickerSize); } @@ -239,22 +283,49 @@ QSize MediaPreviewWidget::currentDimensions() const { return result; } +void MediaPreviewWidget::createLottieIfReady( + not_null document) { + const auto sticker = document->sticker(); + if (!sticker + || !sticker->isLottie() + || _lottie + || !_documentMedia->loaded()) { + return; + } else if (document->isPremiumSticker() + && _documentMedia->videoThumbnailContent().isEmpty()) { + return; + } + const_cast(this)->setupLottie(); +} + void MediaPreviewWidget::setupLottie() { Expects(_document != nullptr); + const auto factor = cIntRetinaFactor(); + const auto size = currentDimensions(); _lottie = std::make_unique( Lottie::ReadContent(_documentMedia->bytes(), _document->filepath()), - Lottie::FrameRequest{ currentDimensions() * cIntRetinaFactor() }, + Lottie::FrameRequest{ size * factor }, Lottie::Quality::High); + if (_document->isPremiumSticker()) { + _effect = std::make_unique( + Lottie::ReadContent(_documentMedia->videoThumbnailContent(), {}), + Lottie::FrameRequest{ size * kPremiumMultiplier * factor }, + Lottie::Quality::High); + } - _lottie->updates( - ) | rpl::start_with_next([=](Lottie::Update update) { + const auto handler = [=](Lottie::Update update) { v::match(update.data, [&](const Lottie::Information &) { this->update(); }, [&](const Lottie::DisplayFrameRequest &) { 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 { @@ -264,9 +335,8 @@ QPixmap MediaPreviewWidget::currentImage() const { const auto webm = sticker && sticker->isWebm(); if (sticker && !webm) { if (_cacheStatus != CacheLoaded) { - if (sticker->isLottie() && !_lottie && _documentMedia->loaded()) { - const_cast(this)->setupLottie(); - } + const_cast(this)->createLottieIfReady( + _document); if (_lottie && _lottie->ready()) { return QPixmap(); } else if (const auto image = _documentMedia->getStickerLarge()) { diff --git a/Telegram/SourceFiles/window/window_media_preview.h b/Telegram/SourceFiles/window/window_media_preview.h index d5a78f840d..686e7560f7 100644 --- a/Telegram/SourceFiles/window/window_media_preview.h +++ b/Telegram/SourceFiles/window/window_media_preview.h @@ -50,10 +50,13 @@ private: void startGifAnimation(const Media::Clip::ReaderPointer &gif); QSize currentDimensions() const; QPixmap currentImage() const; + void createLottieIfReady(not_null document); void setupLottie(); void startShow(); void fillEmojiString(); void resetGifAndCache(); + [[nodiscard]] QPoint innerPosition(QSize size) const; + [[nodiscard]] QPoint outerPosition(QSize size) const; [[nodiscard]] QRect updateArea() const; not_null _controller; @@ -69,6 +72,7 @@ private: bool _gifWithAlpha = false; crl::time _gifLastPosition = 0; std::unique_ptr _lottie; + std::unique_ptr _effect; int _emojiSize; std::vector> _emojiList;