From d96a8d028ad4c84ea8e2e37d94decc1346928ff2 Mon Sep 17 00:00:00 2001 From: John Preston Date: Tue, 25 Jan 2022 12:53:08 +0300 Subject: [PATCH] Support webm stickers in field autocomplete. --- .../chat_helpers/field_autocomplete.cpp | 132 ++++++++++++------ .../chat_helpers/field_autocomplete.h | 7 +- 2 files changed, 92 insertions(+), 47 deletions(-) diff --git a/Telegram/SourceFiles/chat_helpers/field_autocomplete.cpp b/Telegram/SourceFiles/chat_helpers/field_autocomplete.cpp index 10b96ce27..9575eb7b2 100644 --- a/Telegram/SourceFiles/chat_helpers/field_autocomplete.cpp +++ b/Telegram/SourceFiles/chat_helpers/field_autocomplete.cpp @@ -27,6 +27,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "core/application.h" #include "core/core_settings.h" #include "lottie/lottie_single_player.h" +#include "media/clip/media_clip_reader.h" #include "ui/widgets/popup_menu.h" #include "ui/widgets/scroll_area.h" #include "ui/widgets/input_fields.h" @@ -102,8 +103,13 @@ private: QSize stickerBoundingBox() const; void setupLottie(StickerSuggestion &suggestion); + void setupWebm(StickerSuggestion &suggestion); void repaintSticker(not_null document); + void repaintStickerAtIndex(int index); std::shared_ptr getLottieRenderer(); + void clipCallback( + Media::Clip::Notification notification, + not_null document); const not_null _controller; const not_null _parent; @@ -141,6 +147,13 @@ private: }; +struct FieldAutocomplete::StickerSuggestion { + not_null document; + std::shared_ptr documentMedia; + std::unique_ptr lottie; + Media::Clip::ReaderPointer webm; +}; + FieldAutocomplete::FieldAutocomplete( QWidget *parent, not_null controller) @@ -340,7 +353,7 @@ FieldAutocomplete::StickerRows FieldAutocomplete::getStickerSuggestions() { }; }) | ranges::to_vector; for (auto &suggestion : _srows) { - if (!suggestion.animated) { + if (!suggestion.lottie && !suggestion.webm) { continue; } const auto i = ranges::find( @@ -348,7 +361,8 @@ FieldAutocomplete::StickerRows FieldAutocomplete::getStickerSuggestions() { suggestion.document, &StickerSuggestion::document); if (i != end(result)) { - i->animated = std::move(suggestion.animated); + i->lottie = std::move(suggestion.lottie); + i->webm = std::move(suggestion.webm); } } return result; @@ -812,6 +826,7 @@ void FieldAutocomplete::Inner::paintEvent(QPaintEvent *e) { width(), std::min(st::msgMaxWidth / 2, width() / 2)); + const auto now = crl::now(); int32 rows = rowscount(_srows->size(), _stickersPerRow); int32 fromrow = floorclamp(r.y() - st::stickerPanPadding, st::stickerPanSize.height(), 0, rows); int32 torow = ceilclamp(r.y() + r.height() - st::stickerPanPadding, st::stickerPanSize.height(), 0, rows); @@ -825,12 +840,15 @@ void FieldAutocomplete::Inner::paintEvent(QPaintEvent *e) { auto &sticker = (*_srows)[index]; const auto document = sticker.document; const auto &media = sticker.documentMedia; - if (!document->sticker()) continue; + const auto info = document->sticker(); + if (!info) continue; - if (document->sticker()->isLottie() - && !sticker.animated - && media->loaded()) { - setupLottie(sticker); + if (media->loaded()) { + if (info->isLottie() && !sticker.lottie) { + setupLottie(sticker); + } else if (info->isWebm() && !sticker.webm) { + setupWebm(sticker); + } } QPoint pos(st::stickerPanPadding + col * st::stickerPanSize.width(), st::stickerPanPadding + row * st::stickerPanSize.height()); @@ -841,46 +859,34 @@ void FieldAutocomplete::Inner::paintEvent(QPaintEvent *e) { } media->checkStickerSmall(); - auto w = 1; - auto h = 1; - if (sticker.animated && !document->dimensions.isEmpty()) { - const auto request = Lottie::FrameRequest{ stickerBoundingBox() * cIntRetinaFactor() }; - const auto size = request.size(document->dimensions, true) / cIntRetinaFactor(); - w = std::max(size.width(), 1); - h = std::max(size.height(), 1); - } else { - const auto coef = std::min( - std::min( - (st::stickerPanSize.width() - st::roundRadiusSmall * 2) / float64(document->dimensions.width()), - (st::stickerPanSize.height() - st::roundRadiusSmall * 2) / float64(document->dimensions.height())), - 1.); - w = std::max(qRound(coef * document->dimensions.width()), 1); - h = std::max(qRound(coef * document->dimensions.height()), 1); - } - - if (sticker.animated && sticker.animated->ready()) { - const auto frame = sticker.animated->frame(); - const auto size = frame.size() / cIntRetinaFactor(); - const auto ppos = pos + QPoint( - (st::stickerPanSize.width() - size.width()) / 2, - (st::stickerPanSize.height() - size.height()) / 2); + const auto paused = _controller->isGifPausedAtLeastFor( + Window::GifPauseReason::SavedGifs); + const auto size = ChatHelpers::ComputeStickerSize( + document, + stickerBoundingBox()); + const auto ppos = pos + QPoint( + (st::stickerPanSize.width() - size.width()) / 2, + (st::stickerPanSize.height() - size.height()) / 2); + if (sticker.lottie && sticker.lottie->ready()) { + const auto frame = sticker.lottie->frame(); p.drawImage( - QRect(ppos, size), + QRect(ppos, frame.size() / cIntRetinaFactor()), frame); - const auto paused = _controller->isGifPausedAtLeastFor( - Window::GifPauseReason::SavedGifs); if (!paused) { - sticker.animated->markFrameShown(); + sticker.lottie->markFrameShown(); } + } else if (sticker.webm && sticker.webm->started()) { + p.drawPixmap(ppos, sticker.webm->current({ + .frame = size, + .keepAlpha = true, + }, paused ? 0 : now)); } else if (const auto image = media->getStickerSmall()) { - QPoint ppos = pos + QPoint((st::stickerPanSize.width() - w) / 2, (st::stickerPanSize.height() - h) / 2); - p.drawPixmapLeft(ppos, width(), image->pix(w, h)); + p.drawPixmapLeft(ppos, width(), image->pix(size)); } else { - QPoint ppos = pos + QPoint((st::stickerPanSize.width() - w) / 2, (st::stickerPanSize.height() - h) / 2); ChatHelpers::PaintStickerThumbnailPath( p, media.get(), - QRect(ppos, QSize(w, h)), + QRect(ppos, size), _pathGradient.get()); } } @@ -1271,19 +1277,30 @@ auto FieldAutocomplete::Inner::getLottieRenderer() void FieldAutocomplete::Inner::setupLottie(StickerSuggestion &suggestion) { const auto document = suggestion.document; - suggestion.animated = ChatHelpers::LottiePlayerFromDocument( + suggestion.lottie = ChatHelpers::LottiePlayerFromDocument( suggestion.documentMedia.get(), ChatHelpers::StickerLottieSize::InlineResults, stickerBoundingBox() * cIntRetinaFactor(), Lottie::Quality::Default, getLottieRenderer()); - suggestion.animated->updates( + suggestion.lottie->updates( ) | rpl::start_with_next([=] { repaintSticker(document); }, _stickersLifetime); } +void FieldAutocomplete::Inner::setupWebm(StickerSuggestion &suggestion) { + const auto document = suggestion.document; + auto callback = [=](Media::Clip::Notification notification) { + clipCallback(notification, document); + }; + suggestion.webm = Media::Clip::MakeReader( + suggestion.documentMedia->owner()->location(), + suggestion.documentMedia->bytes(), + std::move(callback)); +} + QSize FieldAutocomplete::Inner::stickerBoundingBox() const { return QSize( st::stickerPanSize.width() - st::roundRadiusSmall * 2, @@ -1299,7 +1316,10 @@ void FieldAutocomplete::Inner::repaintSticker( if (i == end(*_srows)) { return; } - const auto index = (i - begin(*_srows)); + repaintStickerAtIndex(i - begin(*_srows)); +} + +void FieldAutocomplete::Inner::repaintStickerAtIndex(int index) { const auto row = (index / _stickersPerRow); const auto col = (index % _stickersPerRow); update( @@ -1309,6 +1329,36 @@ void FieldAutocomplete::Inner::repaintSticker( st::stickerPanSize.height()); } +void FieldAutocomplete::Inner::clipCallback( + Media::Clip::Notification notification, + not_null document) { + const auto i = ranges::find( + *_srows, + document, + &StickerSuggestion::document); + if (i == end(*_srows)) { + return; + } + using namespace Media::Clip; + switch (notification) { + case Notification::Reinit: { + if (!i->webm) { + break; + } else if (i->webm->state() == State::Error) { + i->webm.setBad(); + } else if (i->webm->ready() && !i->webm->started()) { + const auto size = ChatHelpers::ComputeStickerSize( + i->document, + stickerBoundingBox()); + i->webm->start({ .frame = size, .keepAlpha = true }); + } + } break; + + case Notification::Repaint: break; + } + repaintStickerAtIndex(i - begin(*_srows)); +} + void FieldAutocomplete::Inner::selectByMouse(QPoint globalPosition) { _mouseSelection = true; _lastMousePosition = globalPosition; diff --git a/Telegram/SourceFiles/chat_helpers/field_autocomplete.h b/Telegram/SourceFiles/chat_helpers/field_autocomplete.h index f149b5c95..b6a3e0e08 100644 --- a/Telegram/SourceFiles/chat_helpers/field_autocomplete.h +++ b/Telegram/SourceFiles/chat_helpers/field_autocomplete.h @@ -129,12 +129,7 @@ protected: private: class Inner; friend class Inner; - - struct StickerSuggestion { - not_null document; - std::shared_ptr documentMedia; - std::unique_ptr animated; - }; + struct StickerSuggestion; struct MentionRow { not_null user;