From 8e749173de571761f044a38e5c650b66f2667da7 Mon Sep 17 00:00:00 2001 From: John Preston Date: Tue, 25 Jan 2022 00:40:26 +0300 Subject: [PATCH] Render webm stickers in StickersListWidget. --- .../chat_helpers/gifs_list_widget.cpp | 47 ++-- .../chat_helpers/gifs_list_widget.h | 4 +- .../chat_helpers/stickers_list_widget.cpp | 213 ++++++++++---- .../chat_helpers/stickers_list_widget.h | 40 ++- .../chat_helpers/stickers_lottie.cpp | 12 + .../chat_helpers/stickers_lottie.h | 4 + .../inline_bot_layout_internal.cpp | 42 +-- .../media/clip/media_clip_reader.cpp | 262 ++++++++++++------ .../media/clip/media_clip_reader.h | 100 ++----- .../SourceFiles/overview/overview_layout.cpp | 36 ++- .../attach/attach_single_media_preview.cpp | 25 +- .../window/window_media_preview.cpp | 17 +- 12 files changed, 483 insertions(+), 319 deletions(-) diff --git a/Telegram/SourceFiles/chat_helpers/gifs_list_widget.cpp b/Telegram/SourceFiles/chat_helpers/gifs_list_widget.cpp index 6fc3c07f0..924a0c6d0 100644 --- a/Telegram/SourceFiles/chat_helpers/gifs_list_widget.cpp +++ b/Telegram/SourceFiles/chat_helpers/gifs_list_widget.cpp @@ -46,6 +46,8 @@ namespace { constexpr auto kSearchRequestDelay = 400; constexpr auto kInlineItemsMaxPerRow = 5; constexpr auto kSearchBotUsername = "gif"_cs; +constexpr auto kMinRepaintDelay = crl::time(16); +constexpr auto kMinAfterScrollDelay = crl::time(33); } // namespace @@ -173,7 +175,9 @@ GifsListWidget::GifsListWidget( , _mosaic(st::emojiPanWidth - st::inlineResultsLeft) , _previewTimer([=] { showPreview(); }) { setMouseTracking(true); - setAttribute(Qt::WA_OpaquePaintEvent); + + // Otherwise our optimization on repainting is too aggressive. + setAttribute(Qt::WA_OpaquePaintEvent, false); _inlineRequestTimer.setSingleShot(true); connect( @@ -239,7 +243,7 @@ void GifsListWidget::visibleTopBottomUpdated( auto top = getVisibleTop(); Inner::visibleTopBottomUpdated(visibleTop, visibleBottom); if (top != getVisibleTop()) { - _lastScrolled = crl::now(); + _lastScrolledAt = crl::now(); } checkLoadMore(); } @@ -498,7 +502,7 @@ void GifsListWidget::clearSelection() { setCursor(style::cur_default); } _selected = _pressed = -1; - update(); + repaintItems(); } TabbedSelector::InnerFooter *GifsListWidget::getFooter() const { @@ -544,7 +548,7 @@ void GifsListWidget::refreshSavedGifs() { deleteUnusedGifLayouts(); resizeToWidth(width()); - update(); + repaintItems(); } if (isVisible()) { @@ -672,7 +676,7 @@ int GifsListWidget::refreshInlineRows(const InlineCacheEntry *entry, bool result } resizeToWidth(width()); - update(); + repaintItems(); _lastMousePos = QCursor::pos(); updateSelected(); @@ -711,16 +715,13 @@ void GifsListWidget::inlineItemLayoutChanged(const InlineBots::Layout::ItemBase } } -void GifsListWidget::inlineItemRepaint(const InlineBots::Layout::ItemBase *layout) { - auto ms = crl::now(); - if (_lastScrolled + 100 <= ms) { - update(); - } else { - _updateInlineItems.callOnce(_lastScrolled + 100 - ms); - } +void GifsListWidget::inlineItemRepaint( + const InlineBots::Layout::ItemBase *layout) { + updateInlineItems(); } -bool GifsListWidget::inlineItemVisible(const InlineBots::Layout::ItemBase *layout) { +bool GifsListWidget::inlineItemVisible( + const InlineBots::Layout::ItemBase *layout) { auto position = layout->position(); if (position < 0 || !isVisible()) { return false; @@ -930,12 +931,22 @@ void GifsListWidget::showPreview() { } void GifsListWidget::updateInlineItems() { - auto ms = crl::now(); - if (_lastScrolled + 100 <= ms) { - update(); - } else { - _updateInlineItems.callOnce(_lastScrolled + 100 - ms); + const auto now = crl::now(); + + const auto delay = std::max( + _lastScrolledAt + kMinAfterScrollDelay - now, + _lastUpdatedAt + kMinRepaintDelay - now); + if (delay <= 0) { + repaintItems(); + } else if (!_updateInlineItems.isActive() + || _updateInlineItems.remainingTime() > kMinRepaintDelay) { + _updateInlineItems.callOnce(std::max(delay, kMinRepaintDelay)); } } +void GifsListWidget::repaintItems(crl::time now) { + _lastUpdatedAt = now ? now : crl::now(); + update(); +} + } // namespace ChatHelpers diff --git a/Telegram/SourceFiles/chat_helpers/gifs_list_widget.h b/Telegram/SourceFiles/chat_helpers/gifs_list_widget.h index f07044ebb..8bc6e3d23 100644 --- a/Telegram/SourceFiles/chat_helpers/gifs_list_widget.h +++ b/Telegram/SourceFiles/chat_helpers/gifs_list_widget.h @@ -133,12 +133,14 @@ private: void paintInlineItems(Painter &p, QRect clip); void updateInlineItems(); + void repaintItems(crl::time now = 0); void showPreview(); MTP::Sender _api; Section _section = Section::Gifs; - crl::time _lastScrolled = 0; + crl::time _lastScrolledAt = 0; + crl::time _lastUpdatedAt = 0; base::Timer _updateInlineItems; bool _inlineWithThumb = false; diff --git a/Telegram/SourceFiles/chat_helpers/stickers_list_widget.cpp b/Telegram/SourceFiles/chat_helpers/stickers_list_widget.cpp index 778016663..a1e9f86b9 100644 --- a/Telegram/SourceFiles/chat_helpers/stickers_list_widget.cpp +++ b/Telegram/SourceFiles/chat_helpers/stickers_list_widget.cpp @@ -111,7 +111,7 @@ struct StickerIcon { }; -class StickersListWidget::Footer : public TabbedSelector::InnerFooter { +class StickersListWidget::Footer final : public TabbedSelector::InnerFooter { public: explicit Footer( not_null parent, @@ -209,6 +209,16 @@ private: }; +struct StickersListWidget::Sticker { + not_null document; + std::shared_ptr documentMedia; + Lottie::Animation *lottie = nullptr; + Media::Clip::ReaderPointer webm; + QPixmap savedFrame; + + void ensureMediaCreated(); +}; + auto StickersListWidget::PrepareStickers( const QVector &pack) -> std::vector { @@ -284,6 +294,7 @@ void StickersListWidget::Footer::clearHeavyData() { count); for (auto i = 0; i != count; ++i) { auto &icon = _icons[i]; + icon.webm = nullptr; icon.lottie = nullptr; icon.lifetime.destroy(); icon.stickerMedia = nullptr; @@ -737,6 +748,7 @@ void StickersListWidget::Footer::refreshIcons( if (const auto i = indices.find(now.setId); i != end(indices)) { auto &was = _icons[i->second]; if (now.sticker == was.sticker) { + now.webm = std::move(was.webm); now.lottie = std::move(was.lottie); now.lifetime = std::move(was.lifetime); now.savedFrame = std::move(was.savedFrame); @@ -979,7 +991,7 @@ StickersListWidget::StickersListWidget( session().downloaderTaskFinished( ) | rpl::start_with_next([=] { if (isVisible()) { - update(); + repaintItems(); readVisibleFeatured(getVisibleTop(), getVisibleBottom()); } }, lifetime()); @@ -1135,7 +1147,7 @@ void StickersListWidget::preloadMoreOfficial() { } }); resizeToWidth(width()); - update(); + repaintItems(); }).send(); } @@ -1499,8 +1511,8 @@ void StickersListWidget::takeHeavyData(Set &to, Set &from) { } } for (const auto &sticker : fromList) { - if (sticker.animated) { - to.lottiePlayer->remove(sticker.animated); + if (sticker.lottie) { + to.lottiePlayer->remove(sticker.lottie); } } } @@ -1509,7 +1521,8 @@ void StickersListWidget::takeHeavyData(Set &to, Set &from) { void StickersListWidget::takeHeavyData(Sticker &to, Sticker &from) { to.documentMedia = std::move(from.documentMedia); to.savedFrame = std::move(from.savedFrame); - to.animated = base::take(from.animated); + to.lottie = base::take(from.lottie); + to.webm = base::take(from.webm); } auto StickersListWidget::shownSets() const -> const std::vector & { @@ -1629,6 +1642,9 @@ void StickersListWidget::paintStickers(Painter &p, QRect clip) { ? &_pressed : &_selected); + const auto now = crl::now(); + const auto paused = controller()->isGifPausedAtLeastFor( + Window::GifPauseReason::SavedGifs); if (sets.empty() && _section == Section::Search) { paintEmptySearchResults(p); } @@ -1708,9 +1724,11 @@ void StickersListWidget::paintStickers(Painter &p, QRect clip) { auto selected = selectedSticker ? (selectedSticker->section == info.section && selectedSticker->index == index) : false; auto deleteSelected = false; - paintSticker(p, set, info.rowsTop, info.section, index, selected, deleteSelected); + paintSticker(p, set, info.rowsTop, info.section, index, now, paused, selected, deleteSelected); + } + if (!paused) { + markLottieFrameShown(set); } - markLottieFrameShown(set); return true; } if (setHasTitle(set) && clip.top() < info.rowsTop) { @@ -1754,21 +1772,19 @@ void StickersListWidget::paintStickers(Painter &p, QRect clip) { auto selected = selectedSticker ? (selectedSticker->section == info.section && selectedSticker->index == index) : false; auto deleteSelected = selected && selectedSticker->overDelete; - paintSticker(p, set, info.rowsTop, info.section, index, selected, deleteSelected); + paintSticker(p, set, info.rowsTop, info.section, index, now, paused, selected, deleteSelected); } } - markLottieFrameShown(set); + if (!paused) { + markLottieFrameShown(set); + } return true; }); } void StickersListWidget::markLottieFrameShown(Set &set) { if (const auto player = set.lottiePlayer.get()) { - const auto paused = controller()->isGifPausedAtLeastFor( - Window::GifPauseReason::SavedGifs); - if (!paused) { - player->markFrameShown(); - } + player->markFrameShown(); } } @@ -1801,7 +1817,8 @@ void StickersListWidget::clearHeavyIn(Set &set, bool clearSavedFrames) { if (clearSavedFrames) { sticker.savedFrame = QPixmap(); } - sticker.animated = nullptr; + sticker.webm = nullptr; + sticker.lottie = nullptr; sticker.documentMedia = nullptr; } } @@ -1821,7 +1838,7 @@ void StickersListWidget::pauseInvisibleLottieIn(const SectionInfo &info) { if (index >= info.count) { break; } - if (const auto animated = set.stickers[index].animated) { + if (const auto animated = set.stickers[index].lottie) { player->pause(animated); } } @@ -1903,16 +1920,13 @@ void StickersListWidget::ensureLottiePlayer(Set &set) { raw->updates( ) | rpl::start_with_next([=] { + auto &sets = shownSets(); enumerateSections([&](const SectionInfo &info) { - if (shownSets()[info.section].lottiePlayer.get() == raw) { - update( - 0, - info.rowsTop, - width(), - info.rowsBottom - info.rowsTop); - return false; + if (sets[info.section].lottiePlayer.get() != raw) { + return true; } - return true; + repaintItems(info); + return false; }); }, set.lottieLifetime); } @@ -1923,20 +1937,113 @@ void StickersListWidget::setupLottie(Set &set, int section, int index) { // Document should be loaded already for the animation to be set up. Assert(sticker.documentMedia != nullptr); - sticker.animated = LottieAnimationFromDocument( + sticker.lottie = LottieAnimationFromDocument( set.lottiePlayer.get(), sticker.documentMedia.get(), StickerLottieSize::StickersPanel, boundingBoxSize() * cIntRetinaFactor()); } +void StickersListWidget::setupWebm(Set &set, int section, int index) { + auto &sticker = set.stickers[index]; + + // Document should be loaded already for the animation to be set up. + Assert(sticker.documentMedia != nullptr); + const auto setId = set.id; + const auto document = sticker.document; + auto callback = [=](Media::Clip::Notification notification) { + clipCallback(notification, setId, document, index); + }; + sticker.webm = Media::Clip::MakeReader( + sticker.documentMedia->owner()->location(), + sticker.documentMedia->bytes(), + std::move(callback)); +} + +void StickersListWidget::clipCallback( + Media::Clip::Notification notification, + uint64 setId, + not_null document, + int indexHint) { + Expects(indexHint >= 0); + + auto &sets = shownSets(); + enumerateSections([&](const SectionInfo &info) { + auto &set = sets[info.section]; + if (set.id != setId) { + return true; + } + using namespace Media::Clip; + switch (notification) { + case Notification::Reinit: { + const auto j = (indexHint < set.stickers.size() + && set.stickers[indexHint].document == document) + ? (begin(set.stickers) + indexHint) + : ranges::find(set.stickers, document, &Sticker::document); + if (j == end(set.stickers) || !j->webm) { + break; + } + const auto index = j - begin(set.stickers); + auto &webm = j->webm; + if (webm->state() == State::Error) { + webm.setBad(); + } else if (webm->ready() && !webm->started()) { + const auto size = ComputeStickerSize( + j->document, + boundingBoxSize()); + webm->start({ .frame = size, .keepAlpha = true }); + } else if (webm->autoPausedGif() && !itemVisible(info, index)) { + webm = nullptr; + } + } break; + + case Notification::Repaint: break; + } + + repaintItems(info); + return false; + }); +} + +bool StickersListWidget::itemVisible( + const SectionInfo &info, + int index) const { + const auto visibleTop = getVisibleTop(); + const auto visibleBottom = getVisibleBottom(); + const auto row = index / _columnCount; + const auto top = info.rowsTop + row * _singleSize.height(); + const auto bottom = top + _singleSize.height(); + return (visibleTop < bottom) && (visibleBottom > top); +} + +void StickersListWidget::repaintItems(const SectionInfo &info) { + update( + 0, + info.rowsTop, + width(), + info.rowsBottom - info.rowsTop); +} + +void StickersListWidget::repaintItems() { + +} + QSize StickersListWidget::boundingBoxSize() const { return QSize( _singleSize.width() - st::roundRadiusSmall * 2, _singleSize.height() - st::roundRadiusSmall * 2); } -void StickersListWidget::paintSticker(Painter &p, Set &set, int y, int section, int index, bool selected, bool deleteSelected) { +void StickersListWidget::paintSticker( + Painter &p, + Set &set, + int y, + int section, + int index, + crl::time now, + bool paused, + bool selected, + bool deleteSelected) { auto &sticker = set.stickers[index]; sticker.ensureMediaCreated(); const auto document = sticker.document; @@ -1946,10 +2053,13 @@ void StickersListWidget::paintSticker(Painter &p, Set &set, int y, int section, } const auto isLottie = document->sticker()->isLottie(); + const auto isWebm = document->sticker()->isWebm(); if (isLottie - && !sticker.animated + && !sticker.lottie && media->loaded()) { setupLottie(set, section, index); + } else if (isWebm && !sticker.webm && media->loaded()) { + setupWebm(set, section, index); } int row = (index / _columnCount), col = (index % _columnCount); @@ -1963,25 +2073,15 @@ void StickersListWidget::paintSticker(Painter &p, Set &set, int y, int section, media->checkStickerSmall(); - auto w = 1; - auto h = 1; - if (isLottie && !document->dimensions.isEmpty()) { - const auto request = Lottie::FrameRequest{ boundingBoxSize() * cIntRetinaFactor() }; - const auto size = request.size(document->dimensions, true) / cIntRetinaFactor(); - w = std::max(size.width(), 1); - h = std::max(size.height(), 1); - } else { - auto coef = qMin((_singleSize.width() - st::roundRadiusSmall * 2) / float64(document->dimensions.width()), (_singleSize.height() - st::roundRadiusSmall * 2) / float64(document->dimensions.height())); - if (coef > 1) coef = 1; - w = std::max(qRound(coef * document->dimensions.width()), 1); - h = std::max(qRound(coef * document->dimensions.height()), 1); - } - auto ppos = pos + QPoint((_singleSize.width() - w) / 2, (_singleSize.height() - h) / 2); + const auto size = ComputeStickerSize(document, boundingBoxSize()); + const auto ppos = pos + QPoint( + (_singleSize.width() - size.width()) / 2, + (_singleSize.height() - size.height()) / 2); - if (sticker.animated && sticker.animated->ready()) { + if (sticker.lottie && sticker.lottie->ready()) { auto request = Lottie::FrameRequest(); request.box = boundingBoxSize() * cIntRetinaFactor(); - const auto frame = sticker.animated->frame(request); + const auto frame = sticker.lottie->frame(request); p.drawImage( QRect(ppos, frame.size() / cIntRetinaFactor()), frame); @@ -1989,13 +2089,20 @@ void StickersListWidget::paintSticker(Painter &p, Set &set, int y, int section, sticker.savedFrame = QPixmap::fromImage(frame, Qt::ColorOnly); sticker.savedFrame.setDevicePixelRatio(cRetinaFactor()); } - set.lottiePlayer->unpause(sticker.animated); + set.lottiePlayer->unpause(sticker.lottie); + } else if (sticker.webm && sticker.webm->ready()) { + p.drawPixmapLeft( + ppos, + width(), + sticker.webm->current( + { .frame = size, .keepAlpha = true }, + now)); } else { const auto image = media->getStickerSmall(); const auto pixmap = !sticker.savedFrame.isNull() ? sticker.savedFrame : image - ? image->pixSingle(w, h, { .outer = { w, h } }) + ? image->pixSingle(size, { .outer = size }) : QPixmap(); if (!pixmap.isNull()) { p.drawPixmapLeft(ppos, width(), pixmap); @@ -2006,7 +2113,7 @@ void StickersListWidget::paintSticker(Painter &p, Set &set, int y, int section, PaintStickerThumbnailPath( p, media.get(), - QRect(ppos, QSize{ w, h }), + QRect(ppos, size), _pathGradient.get()); } } @@ -2227,7 +2334,7 @@ void StickersListWidget::mouseReleaseEvent(QMouseEvent *e) { auto pressed = _pressed; setPressed(v::null); if (pressed != _selected) { - update(); + repaintItems(); } auto activated = ClickHandler::unpressed(); @@ -2329,7 +2436,7 @@ void StickersListWidget::removeRecentSticker(int section, int index) { if (refresh) { refreshRecentStickers(); updateSelected(); - update(); + repaintItems(); } } @@ -2389,7 +2496,7 @@ void StickersListWidget::enterFromChildEvent(QEvent *e, QWidget *child) { void StickersListWidget::clearSelection() { setPressed(v::null); setSelected(v::null); - update(); + repaintItems(); } TabbedSelector::InnerFooter *StickersListWidget::getFooter() const { @@ -2449,7 +2556,7 @@ void StickersListWidget::refreshStickers() { _lastMousePosition = QCursor::pos(); updateSelected(); - update(); + repaintItems(); } void StickersListWidget::refreshMySets() { @@ -3031,7 +3138,7 @@ void StickersListWidget::showStickerSet(uint64 setId) { if (_footer) { _footer->refreshIcons(ValidateIconAnimations::Scroll); } - update(); + repaintItems(); } scrollTo(0); @@ -3063,7 +3170,7 @@ void StickersListWidget::showStickerSet(uint64 setId) { _lastMousePosition = QCursor::pos(); - update(); + repaintItems(); } void StickersListWidget::refreshMegagroupSetGeometry() { diff --git a/Telegram/SourceFiles/chat_helpers/stickers_list_widget.h b/Telegram/SourceFiles/chat_helpers/stickers_list_widget.h index 225f569ae..80a81136b 100644 --- a/Telegram/SourceFiles/chat_helpers/stickers_list_widget.h +++ b/Telegram/SourceFiles/chat_helpers/stickers_list_widget.h @@ -39,6 +39,11 @@ class DocumentMedia; class StickersSet; } // namespace Data +namespace Media::Clip { +class ReaderPointer; +enum class Notification; +} // namespace Media::Clip + namespace ChatHelpers { struct StickerIcon; @@ -113,6 +118,7 @@ protected: private: class Footer; + struct Sticker; enum class Section { Featured, @@ -178,15 +184,6 @@ private: int rowsBottom = 0; }; - struct Sticker { - not_null document; - std::shared_ptr documentMedia; - Lottie::Animation *animated = nullptr; - QPixmap savedFrame; - - void ensureMediaCreated(); - }; - struct Set { Set( uint64 id, @@ -279,11 +276,27 @@ private: void paintStickers(Painter &p, QRect clip); void paintMegagroupEmptySet(Painter &p, int y, bool buttonSelected); - void paintSticker(Painter &p, Set &set, int y, int section, int index, bool selected, bool deleteSelected); + void paintSticker( + Painter &p, + Set &set, + int y, + int section, + int index, + crl::time now, + bool paused, + bool selected, + bool deleteSelected); void paintEmptySearchResults(Painter &p); void ensureLottiePlayer(Set &set); void setupLottie(Set &set, int section, int index); + void setupWebm(Set &set, int section, int index); + void clipCallback( + Media::Clip::Notification notification, + uint64 setId, + not_null document, + int indexHint); + [[nodiscard]] bool itemVisible(const SectionInfo &info, int index) const; void markLottieFrameShown(Set &set); void checkVisibleLottie(); void pauseInvisibleLottieIn(const SectionInfo &info); @@ -292,6 +305,8 @@ private: void takeHeavyData(Sticker &to, Sticker &from); void clearHeavyIn(Set &set, bool clearSavedFrames = true); void clearHeavyData(); + void repaintItems(); + void repaintItems(const SectionInfo &info); int stickersRight() const; bool featuredHasAddButton(int index) const; @@ -302,8 +317,8 @@ private: void refreshMegagroupSetGeometry(); QRect megagroupSetButtonRectFinal() const; - const Data::StickersSetsOrder &defaultSetsOrder() const; - Data::StickersSetsOrder &defaultSetsOrderRef(); + [[nodiscard]] const Data::StickersSetsOrder &defaultSetsOrder() const; + [[nodiscard]] Data::StickersSetsOrder &defaultSetsOrderRef(); enum class AppendSkip { None, @@ -316,7 +331,6 @@ private: bool externalLayout, AppendSkip skip = AppendSkip::None); - void selectEmoji(EmojiPtr emoji); int stickersLeft() const; QRect stickerRect(int section, int sel); diff --git a/Telegram/SourceFiles/chat_helpers/stickers_lottie.cpp b/Telegram/SourceFiles/chat_helpers/stickers_lottie.cpp index b7d45af42..712bcea77 100644 --- a/Telegram/SourceFiles/chat_helpers/stickers_lottie.cpp +++ b/Telegram/SourceFiles/chat_helpers/stickers_lottie.cpp @@ -15,6 +15,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "data/data_session.h" #include "data/data_file_origin.h" #include "storage/cache/storage_cache_database.h" +#include "history/view/media/history_view_media_common.h" #include "media/clip/media_clip_reader.h" #include "ui/effects/path_shift_gradient.h" #include "main/main_session.h" @@ -276,4 +277,15 @@ bool PaintStickerThumbnailPath( }); } +QSize ComputeStickerSize(not_null document, QSize box) { + const auto sticker = document->sticker(); + const auto dimensions = document->dimensions; + if (!sticker || !sticker->isLottie() || dimensions.isEmpty()) { + return HistoryView::DownscaledSize(dimensions, box); + } + const auto ratio = style::DevicePixelRatio(); + const auto request = Lottie::FrameRequest{ box * ratio }; + return HistoryView::NonEmptySize(request.size(dimensions, true) / ratio); +} + } // namespace ChatHelpers diff --git a/Telegram/SourceFiles/chat_helpers/stickers_lottie.h b/Telegram/SourceFiles/chat_helpers/stickers_lottie.h index 20407423a..0e95a806a 100644 --- a/Telegram/SourceFiles/chat_helpers/stickers_lottie.h +++ b/Telegram/SourceFiles/chat_helpers/stickers_lottie.h @@ -113,4 +113,8 @@ bool PaintStickerThumbnailPath( QRect target, not_null gradient); +[[nodiscard]] QSize ComputeStickerSize( + not_null document, + QSize box); + } // namespace ChatHelpers diff --git a/Telegram/SourceFiles/inline_bots/inline_bot_layout_internal.cpp b/Telegram/SourceFiles/inline_bots/inline_bot_layout_internal.cpp index 64370851e..f9ea7b520 100644 --- a/Telegram/SourceFiles/inline_bots/inline_bot_layout_internal.cpp +++ b/Telegram/SourceFiles/inline_bots/inline_bot_layout_internal.cpp @@ -168,19 +168,20 @@ void Gif::paint(Painter &p, const QRect &clip, const PaintContext *context) cons } const auto radial = isRadialAnimation(); - int32 height = st::inlineMediaHeight; - QSize frame = countFrameSize(); - - QRect r(0, 0, _width, height); + const auto frame = countFrameSize(); + const auto r = QRect(0, 0, _width, st::inlineMediaHeight); if (animating) { - const auto pixmap = _gif->current(frame.width(), frame.height(), _width, height, ImageRoundRadius::None, RectPart::None, context->paused ? 0 : context->ms); + const auto pixmap = _gif->current({ + .frame = frame, + .outer = r.size(), + }, context->paused ? 0 : context->ms); if (_thumb.isNull()) { _thumb = pixmap; _thumbGood = true; } p.drawPixmap(r.topLeft(), pixmap); } else { - prepareThumbnail({ _width, height }, frame); + prepareThumbnail(r.size(), frame); if (_thumb.isNull()) { p.fillRect(r, st::overviewPhotoBg); } else { @@ -211,7 +212,11 @@ void Gif::paint(Painter &p, const QRect &clip, const PaintContext *context) cons return &st::historyFileInDownload; }(); const auto size = st::inlineRadialSize; - QRect inner((_width - size) / 2, (height - size) / 2, size, size); + QRect inner( + (r.width() - size) / 2, + (r.height() - size) / 2, + size, + size); icon->paintInCenter(p, inner); if (radial) { p.setOpacity(1); @@ -404,9 +409,10 @@ void Gif::clipCallback(Media::Clip::Notification notification) { _gif->height()); _gif.reset(); } else { - auto height = st::inlineMediaHeight; - auto frame = countFrameSize(); - _gif->start(frame.width(), frame.height(), _width, height, ImageRoundRadius::None, RectPart::None); + _gif->start({ + .frame = countFrameSize(), + .outer = { _width, st::inlineMediaHeight }, + }); } } else if (_gif->autoPausedGif() && !context()->inlineItemVisible(this)) { unloadHeavyPart(); @@ -1424,7 +1430,10 @@ void Game::paint(Painter &p, const QRect &clip, const PaintContext *context) con radial = isRadialAnimation(); if (animating) { - const auto pixmap = _gif->current(_frameSize.width(), _frameSize.height(), st::inlineThumbSize, st::inlineThumbSize, ImageRoundRadius::None, RectPart::None, context->paused ? 0 : context->ms); + const auto pixmap = _gif->current({ + .frame = _frameSize, + .outer = { st::inlineThumbSize, st::inlineThumbSize }, + }, context->paused ? 0 : context->ms); if (_thumb.isNull()) { _thumb = pixmap; _thumbGood = true; @@ -1594,13 +1603,10 @@ void Game::clipCallback(Media::Clip::Notification notification) { _gif->height()); _gif.reset(); } else { - _gif->start( - _frameSize.width(), - _frameSize.height(), - st::inlineThumbSize, - st::inlineThumbSize, - ImageRoundRadius::None, - RectPart::None); + _gif->start({ + .frame = _frameSize, + .outer = { st::inlineThumbSize, st::inlineThumbSize }, + }); } } else if (_gif->autoPausedGif() && !context()->inlineItemVisible(this)) { unloadHeavyPart(); diff --git a/Telegram/SourceFiles/media/clip/media_clip_reader.cpp b/Telegram/SourceFiles/media/clip/media_clip_reader.cpp index e0ccdfa0f..26700cc6b 100644 --- a/Telegram/SourceFiles/media/clip/media_clip_reader.cpp +++ b/Telegram/SourceFiles/media/clip/media_clip_reader.cpp @@ -35,44 +35,49 @@ constexpr auto kClipThreadsCount = 8; constexpr auto kAverageGifSize = 320 * 240; constexpr auto kWaitBeforeGifPause = crl::time(200); -QVector threads; -QVector managers; - QImage PrepareFrameImage(const FrameRequest &request, const QImage &original, bool hasAlpha, QImage &cache) { - auto needResize = (original.width() != request.framew) || (original.height() != request.frameh); - auto needOuterFill = (request.outerw != request.framew) || (request.outerh != request.frameh); - auto needRounding = (request.radius != ImageRoundRadius::None); + const auto needResize = (original.size() != request.frame); + const auto needOuterFill = request.outer.isValid() && (request.outer != request.frame); + const auto needRounding = (request.radius != ImageRoundRadius::None); if (!needResize && !needOuterFill && !hasAlpha && !needRounding) { return original; } - auto factor = request.factor; - auto needNewCache = (cache.width() != request.outerw || cache.height() != request.outerh); + const auto factor = request.factor; + const auto size = request.outer.isValid() ? request.outer : request.frame; + const auto needNewCache = (cache.size() != size); if (needNewCache) { - cache = QImage(request.outerw, request.outerh, QImage::Format_ARGB32_Premultiplied); + cache = QImage(size, QImage::Format_ARGB32_Premultiplied); cache.setDevicePixelRatio(factor); } + if (hasAlpha && request.keepAlpha) { + cache.fill(Qt::transparent); + } { Painter p(&cache); - if (needNewCache) { - if (request.framew < request.outerw) { - p.fillRect(0, 0, (request.outerw - request.framew) / (2 * factor), cache.height() / factor, st::imageBg); - p.fillRect((request.outerw - request.framew) / (2 * factor) + (request.framew / factor), 0, (cache.width() / factor) - ((request.outerw - request.framew) / (2 * factor) + (request.framew / factor)), cache.height() / factor, st::imageBg); + const auto framew = request.frame.width(); + const auto outerw = size.width(); + const auto frameh = request.frame.height(); + const auto outerh = size.height(); + if (needNewCache && (!hasAlpha || !request.keepAlpha)) { + if (framew < outerw) { + p.fillRect(0, 0, (outerw - framew) / (2 * factor), cache.height() / factor, st::imageBg); + p.fillRect((outerw - framew) / (2 * factor) + (framew / factor), 0, (cache.width() / factor) - ((outerw - framew) / (2 * factor) + (framew / factor)), cache.height() / factor, st::imageBg); } - if (request.frameh < request.outerh) { - p.fillRect(qMax(0, (request.outerw - request.framew) / (2 * factor)), 0, qMin(cache.width(), request.framew) / factor, (request.outerh - request.frameh) / (2 * factor), st::imageBg); - p.fillRect(qMax(0, (request.outerw - request.framew) / (2 * factor)), (request.outerh - request.frameh) / (2 * factor) + (request.frameh / factor), qMin(cache.width(), request.framew) / factor, (cache.height() / factor) - ((request.outerh - request.frameh) / (2 * factor) + (request.frameh / factor)), st::imageBg); + if (frameh < outerh) { + p.fillRect(qMax(0, (outerw - framew) / (2 * factor)), 0, qMin(cache.width(), framew) / factor, (outerh - frameh) / (2 * factor), st::imageBg); + p.fillRect(qMax(0, (outerw - framew) / (2 * factor)), (outerh - frameh) / (2 * factor) + (frameh / factor), qMin(cache.width(), framew) / factor, (cache.height() / factor) - ((outerh - frameh) / (2 * factor) + (frameh / factor)), st::imageBg); } } - if (hasAlpha) { - p.fillRect(qMax(0, (request.outerw - request.framew) / (2 * factor)), qMax(0, (request.outerh - request.frameh) / (2 * factor)), qMin(cache.width(), request.framew) / factor, qMin(cache.height(), request.frameh) / factor, st::imageBgTransparent); + if (hasAlpha && !request.keepAlpha) { + p.fillRect(qMax(0, (outerw - framew) / (2 * factor)), qMax(0, (outerh - frameh) / (2 * factor)), qMin(cache.width(), framew) / factor, qMin(cache.height(), frameh) / factor, st::imageBgTransparent); } - auto position = QPoint((request.outerw - request.framew) / (2 * factor), (request.outerh - request.frameh) / (2 * factor)); + const auto position = QPoint((outerw - framew) / (2 * factor), (outerh - frameh) / (2 * factor)); if (needResize) { PainterHighQualityEnabler hq(p); - auto dst = QRect(position, QSize(request.framew / factor, request.frameh / factor)); - auto src = QRect(0, 0, original.width(), original.height()); + const auto dst = QRect(position, QSize(framew / factor, frameh / factor)); + const auto src = QRect(0, 0, original.width(), original.height()); p.drawImage(dst, original, src, Qt::ColorOnly); } else { p.drawImage(position, original); @@ -93,6 +98,81 @@ QPixmap PrepareFrame(const FrameRequest &request, const QImage &original, bool h } // namespace +enum class ProcessResult { + Error, + Started, + Finished, + Paused, + Repaint, + CopyFrame, + Wait, +}; + +class Manager final : public QObject { +public: + explicit Manager(not_null thread); + ~Manager(); + + int loadLevel() const { + return _loadLevel; + } + void append(Reader *reader, const Core::FileLocation &location, const QByteArray &data); + void start(Reader *reader); + void update(Reader *reader); + void stop(Reader *reader); + bool carries(Reader *reader) const; + +private: + void process(); + void finish(); + void callback(Reader *reader, Notification notification); + void clear(); + + QAtomicInt _loadLevel; + using ReaderPointers = QMap; + ReaderPointers _readerPointers; + mutable QMutex _readerPointersMutex; + + ReaderPointers::const_iterator constUnsafeFindReaderPointer(ReaderPrivate *reader) const; + ReaderPointers::iterator unsafeFindReaderPointer(ReaderPrivate *reader); + + bool handleProcessResult(ReaderPrivate *reader, ProcessResult result, crl::time ms); + + enum ResultHandleState { + ResultHandleRemove, + ResultHandleStop, + ResultHandleContinue, + }; + ResultHandleState handleResult(ReaderPrivate *reader, ProcessResult result, crl::time ms); + + using Readers = QMap; + Readers _readers; + + QTimer _timer; + QThread *_processingInThread = nullptr; + bool _needReProcess = false; + +}; + +namespace { + +struct Worker { + Worker() : manager(&thread) { + thread.start(); + } + ~Worker() { + thread.quit(); + thread.wait(); + } + + QThread thread; + Manager manager; +}; + +std::vector> Workers; + +} // namespace + Reader::Reader( const Core::FileLocation &location, const QByteArray &data, @@ -112,23 +192,21 @@ Reader::Reader(const QByteArray &data, Callback &&callback) } void Reader::init(const Core::FileLocation &location, const QByteArray &data) { - if (threads.size() < kClipThreadsCount) { - _threadIndex = threads.size(); - threads.push_back(new QThread()); - managers.push_back(new Manager(threads.back())); - threads.back()->start(); + if (Workers.size() < kClipThreadsCount) { + _threadIndex = Workers.size(); + Workers.push_back(std::make_unique()); } else { - _threadIndex = int32(base::RandomValue() % threads.size()); - int32 loadLevel = 0x7FFFFFFF; - for (int32 i = 0, l = threads.size(); i < l; ++i) { - int32 level = managers.at(i)->loadLevel(); + _threadIndex = base::RandomIndex(Workers.size()); + auto loadLevel = 0x7FFFFFFF; + for (int i = 0, l = int(Workers.size()); i < l; ++i) { + const auto level = Workers[i]->manager.loadLevel(); if (level < loadLevel) { _threadIndex = i; loadLevel = level; } } } - managers.at(_threadIndex)->append(this, location, data); + Workers[_threadIndex]->manager.append(this, location, data); } Reader::Frame *Reader::frameToShow(int32 *index) const { // 0 means not ready @@ -207,65 +285,74 @@ void Reader::SafeCallback( int threadIndex, Notification notification) { // Check if reader is not deleted already - if (managers.size() > threadIndex && managers.at(threadIndex)->carries(reader) && reader->_callback) { + if (Workers.size() > threadIndex + && Workers[threadIndex]->manager.carries(reader) + && reader->_callback) { reader->_callback(Notification(notification)); } } -void Reader::start(int32 framew, int32 frameh, int32 outerw, int32 outerh, ImageRoundRadius radius, RectParts corners) { - if (managers.size() <= _threadIndex) error(); - if (_state == State::Error) return; - - if (_step.loadAcquire() == kWaitingForRequestStep) { - int factor = style::DevicePixelRatio(); - FrameRequest request; - request.factor = factor; - request.framew = framew * factor; - request.frameh = frameh * factor; - request.outerw = outerw * factor; - request.outerh = outerh * factor; - request.radius = radius; - request.corners = corners; - _frames[0].request = _frames[1].request = _frames[2].request = request; - moveToNextShow(); - managers.at(_threadIndex)->start(this); +void Reader::start(FrameRequest request) { + if (Workers.size() <= _threadIndex) { + error(); } + if (_state == State::Error + || (_step.loadAcquire() != kWaitingForRequestStep)) { + return; + } + const auto factor = style::DevicePixelRatio(); + request.factor = factor; + request.frame *= factor; + if (request.outer.isValid()) { + request.outer *= factor; + } + _frames[0].request = _frames[1].request = _frames[2].request = request; + moveToNextShow(); + Workers[_threadIndex]->manager.start(this); } -QPixmap Reader::current(int32 framew, int32 frameh, int32 outerw, int32 outerh, ImageRoundRadius radius, RectParts corners, crl::time ms) { - Expects(outerw > 0); - Expects(outerh > 0); +QPixmap Reader::current(FrameRequest request, crl::time now) { + Expects(!(request.outer.isValid() + ? request.outer + : request.frame).isEmpty()); - auto frame = frameToShow(); + const auto frame = frameToShow(); Assert(frame != nullptr); - auto shouldBePaused = !ms; + const auto shouldBePaused = !now; if (!shouldBePaused) { frame->displayed.storeRelease(1); if (_autoPausedGif.loadAcquire()) { _autoPausedGif.storeRelease(0); - if (managers.size() <= _threadIndex) error(); - if (_state != State::Error) { - managers.at(_threadIndex)->update(this); + if (Workers.size() <= _threadIndex) { + error(); + } else if (_state != State::Error) { + Workers[_threadIndex]->manager.update(this); } } } else { frame->displayed.storeRelease(-1); } - auto factor = style::DevicePixelRatio(); - if (frame->pix.width() == outerw * factor - && frame->pix.height() == outerh * factor - && frame->request.radius == radius - && frame->request.corners == corners) { + const auto factor = style::DevicePixelRatio(); + request.factor = factor; + request.frame *= factor; + if (request.outer.isValid()) { + request.outer *= factor; + } + const auto size = request.outer.isValid() + ? request.outer + : request.frame; + Assert(frame->request.radius == request.radius + && frame->request.corners == request.corners + && frame->request.keepAlpha == request.keepAlpha); + if (frame->pix.size() == size) { moveToNextShow(); return frame->pix; } - frame->request.framew = framew * factor; - frame->request.frameh = frameh * factor; - frame->request.outerw = outerw * factor; - frame->request.outerh = outerh * factor; + frame->request.frame = request.frame; + frame->request.outer = request.outer; QImage cacheForResize; frame->original.setDevicePixelRatio(factor); @@ -277,18 +364,21 @@ QPixmap Reader::current(int32 framew, int32 frameh, int32 outerw, int32 outerh, moveToNextShow(); - if (managers.size() <= _threadIndex) error(); - if (_state != State::Error) { - managers.at(_threadIndex)->update(this); + if (Workers.size() <= _threadIndex) { + error(); + } else if (_state != State::Error) { + Workers[_threadIndex]->manager.update(this); } return frame->pix; } bool Reader::ready() const { - if (_width && _height) return true; + if (_width && _height) { + return true; + } - auto frame = frameToShow(); + const auto frame = frameToShow(); if (frame) { _width = frame->original.width(); _height = frame->original.height(); @@ -298,7 +388,7 @@ bool Reader::ready() const { } crl::time Reader::getPositionMs() const { - if (auto frame = frameToShow()) { + if (const auto frame = frameToShow()) { return frame->positionMs; } return 0; @@ -309,11 +399,13 @@ crl::time Reader::getDurationMs() const { } void Reader::pauseResumeVideo() { - if (managers.size() <= _threadIndex) error(); + if (Workers.size() <= _threadIndex) { + error(); + } if (_state == State::Error) return; _videoPauseRequest.storeRelease(1 - _videoPauseRequest.loadAcquire()); - managers.at(_threadIndex)->start(this); + Workers[_threadIndex]->manager.start(this); } bool Reader::videoPaused() const { @@ -333,9 +425,11 @@ State Reader::state() const { } void Reader::stop() { - if (managers.size() <= _threadIndex) error(); + if (Workers.size() <= _threadIndex) { + error(); + } if (_state != State::Error) { - managers.at(_threadIndex)->stop(this); + Workers[_threadIndex]->manager.stop(this); _width = _height = 0; } } @@ -461,7 +555,7 @@ public: bool renderFrame() { Expects(_request.valid()); - if (!_implementation->renderFrame(frame()->original, frame()->alpha, QSize(_request.framew, _request.frameh))) { + if (!_implementation->renderFrame(frame()->original, frame()->alpha, _request.frame)) { return false; } frame()->original.setDevicePixelRatio(_request.factor); @@ -574,7 +668,7 @@ private: }; -Manager::Manager(QThread *thread) { +Manager::Manager(not_null thread) { moveToThread(thread); connect(thread, &QThread::started, this, [=] { process(); }); connect(thread, &QThread::finished, this, [=] { finish(); }); @@ -884,17 +978,7 @@ Ui::PreparedFileInformation::Video PrepareForSending(const QString &fname, const } void Finish() { - if (!threads.isEmpty()) { - for (int32 i = 0, l = threads.size(); i < l; ++i) { - threads.at(i)->quit(); - DEBUG_LOG(("Waiting for clipThread to finish: %1").arg(i)); - threads.at(i)->wait(); - delete managers.at(i); - delete threads.at(i); - } - threads.clear(); - managers.clear(); - } + Workers.clear(); } Reader *const ReaderPointer::BadPointer = reinterpret_cast(1); diff --git a/Telegram/SourceFiles/media/clip/media_clip_reader.h b/Telegram/SourceFiles/media/clip/media_clip_reader.h index 430acb28d..ad7c5c9bc 100644 --- a/Telegram/SourceFiles/media/clip/media_clip_reader.h +++ b/Telegram/SourceFiles/media/clip/media_clip_reader.h @@ -30,13 +30,12 @@ struct FrameRequest { bool valid() const { return factor > 0; } + QSize frame; + QSize outer; int factor = 0; - int framew = 0; - int frameh = 0; - int outerw = 0; - int outerh = 0; ImageRoundRadius radius = ImageRoundRadius::None; RectParts corners = RectPart::AllCorners; + bool keepAlpha = false; }; // Before ReaderPrivate read the first image and got the original frame size. @@ -54,6 +53,7 @@ enum class Notification { Repaint, }; +class Manager; class ReaderPrivate; class Reader { public: @@ -73,40 +73,40 @@ public: int threadIndex, Notification notification); - void start(int framew, int frameh, int outerw, int outerh, ImageRoundRadius radius, RectParts corners); - QPixmap current(int framew, int frameh, int outerw, int outerh, ImageRoundRadius radius, RectParts corners, crl::time ms); - QPixmap frameOriginal() const { - if (auto frame = frameToShow()) { + void start(FrameRequest request); + [[nodiscard]] QPixmap current(FrameRequest request, crl::time now); + [[nodiscard]] QPixmap frameOriginal() const { + if (const auto frame = frameToShow()) { auto result = QPixmap::fromImage(frame->original); result.detach(); return result; } return QPixmap(); } - bool currentDisplayed() const { - auto frame = frameToShow(); - return frame ? (frame->displayed.loadAcquire() != 0) : true; + [[nodiscard]] bool currentDisplayed() const { + const auto frame = frameToShow(); + return !frame || (frame->displayed.loadAcquire() != 0); } - bool autoPausedGif() const { + [[nodiscard]] bool autoPausedGif() const { return _autoPausedGif.loadAcquire(); } - bool videoPaused() const; - int threadIndex() const { + [[nodiscard]] bool videoPaused() const; + [[nodiscard]] int threadIndex() const { return _threadIndex; } - int width() const; - int height() const; + [[nodiscard]] int width() const; + [[nodiscard]] int height() const; - State state() const; - bool started() const { - auto step = _step.loadAcquire(); + [[nodiscard]] State state() const; + [[nodiscard]] bool started() const { + const auto step = _step.loadAcquire(); return (step == kWaitingForFirstFrameStep) || (step >= 0); } - bool ready() const; + [[nodiscard]] bool ready() const; - crl::time getPositionMs() const; - crl::time getDurationMs() const; + [[nodiscard]] crl::time getPositionMs() const; + [[nodiscard]] crl::time getDurationMs() const; void pauseResumeVideo(); void stop(); @@ -217,62 +217,6 @@ inline ReaderPointer MakeReader(Args&&... args) { return ReaderPointer(new Reader(std::forward(args)...)); } -enum class ProcessResult { - Error, - Started, - Finished, - Paused, - Repaint, - CopyFrame, - Wait, -}; - -class Manager : public QObject { -public: - explicit Manager(QThread *thread); - ~Manager(); - - int loadLevel() const { - return _loadLevel; - } - void append(Reader *reader, const Core::FileLocation &location, const QByteArray &data); - void start(Reader *reader); - void update(Reader *reader); - void stop(Reader *reader); - bool carries(Reader *reader) const; - -private: - void process(); - void finish(); - void callback(Reader *reader, Notification notification); - void clear(); - - QAtomicInt _loadLevel; - using ReaderPointers = QMap; - ReaderPointers _readerPointers; - mutable QMutex _readerPointersMutex; - - ReaderPointers::const_iterator constUnsafeFindReaderPointer(ReaderPrivate *reader) const; - ReaderPointers::iterator unsafeFindReaderPointer(ReaderPrivate *reader); - - bool handleProcessResult(ReaderPrivate *reader, ProcessResult result, crl::time ms); - - enum ResultHandleState { - ResultHandleRemove, - ResultHandleStop, - ResultHandleContinue, - }; - ResultHandleState handleResult(ReaderPrivate *reader, ProcessResult result, crl::time ms); - - using Readers = QMap; - Readers _readers; - - QTimer _timer; - QThread *_processingInThread = nullptr; - bool _needReProcess = false; - -}; - [[nodiscard]] Ui::PreparedFileInformation::Video PrepareForSending( const QString &fname, const QByteArray &data); diff --git a/Telegram/SourceFiles/overview/overview_layout.cpp b/Telegram/SourceFiles/overview/overview_layout.cpp index c88648948..e52f9950e 100644 --- a/Telegram/SourceFiles/overview/overview_layout.cpp +++ b/Telegram/SourceFiles/overview/overview_layout.cpp @@ -1910,12 +1910,10 @@ void Gif::clipCallback(Media::Clip::Notification notification) { } else { auto height = st::inlineMediaHeight; auto frame = countFrameSize(); - _gif->start( - frame.width(), - frame.height(), - _width, - height, - ImageRoundRadius::None, RectPart::None); + _gif->start({ + .frame = countFrameSize(), + .outer = { _width, st::inlineMediaHeight }, + }); } } else if (_gif->autoPausedGif() && !delegate()->itemVisible(this)) { @@ -1997,26 +1995,20 @@ void Gif::paint( } const auto radial = isRadialAnimation(); - int32 height = st::inlineMediaHeight; - QSize frame = countFrameSize(); - - QRect r(0, 0, _width, height); + const auto frame = countFrameSize(); + const auto r = QRect(0, 0, _width, st::inlineMediaHeight); if (animating) { - const auto pixmap = _gif->current( - frame.width(), - frame.height(), - _width, - height, - ImageRoundRadius::None, - RectPart::None, - /*context->paused ? 0 : */context->ms); + const auto pixmap = _gif->current({ + .frame = frame, + .outer = r.size(), + }, /*context->paused ? 0 : */context->ms); if (_thumb.isNull()) { _thumb = pixmap; _thumbGood = true; } p.drawPixmap(r.topLeft(), pixmap); } else { - prepareThumbnail({ _width, height }, frame); + prepareThumbnail(r.size(), frame); if (_thumb.isNull()) { p.fillRect(r, st::overviewPhotoBg); } else { @@ -2044,7 +2036,11 @@ void Gif::paint( return &st::historyFileInDownload; }(); const auto size = st::overviewVideoRadialSize; - QRect inner((_width - size) / 2, (height - size) / 2, size, size); + QRect inner( + (r.width() - size) / 2, + (r.height() - size) / 2, + size, + size); icon->paintInCenter(p, inner); if (radial) { p.setOpacity(1); diff --git a/Telegram/SourceFiles/ui/chat/attach/attach_single_media_preview.cpp b/Telegram/SourceFiles/ui/chat/attach/attach_single_media_preview.cpp index e1d1a51f1..ca688662c 100644 --- a/Telegram/SourceFiles/ui/chat/attach/attach_single_media_preview.cpp +++ b/Telegram/SourceFiles/ui/chat/attach/attach_single_media_preview.cpp @@ -74,16 +74,10 @@ bool SingleMediaPreview::drawBackground() const { bool SingleMediaPreview::tryPaintAnimation(Painter &p) { if (_gifPreview && _gifPreview->started()) { - auto s = QSize(previewWidth(), previewHeight()); - auto paused = _gifPaused(); - auto frame = _gifPreview->current( - s.width(), - s.height(), - s.width(), - s.height(), - ImageRoundRadius::None, - RectPart::None, - paused ? 0 : crl::now()); + const auto paused = _gifPaused(); + const auto frame = _gifPreview->current({ + .frame = QSize(previewWidth(), previewHeight()), + }, paused ? 0 : crl::now()); p.drawPixmap(previewLeft(), previewTop(), frame); return true; } else if (_lottiePreview && _lottiePreview->ready()) { @@ -139,14 +133,9 @@ void SingleMediaPreview::clipCallback( } if (_gifPreview && _gifPreview->ready() && !_gifPreview->started()) { - const auto s = QSize(previewWidth(), previewHeight()); - _gifPreview->start( - s.width(), - s.height(), - s.width(), - s.height(), - ImageRoundRadius::None, - RectPart::None); + _gifPreview->start({ + .frame = QSize(previewWidth(), previewHeight()), + }); } update(); diff --git a/Telegram/SourceFiles/window/window_media_preview.cpp b/Telegram/SourceFiles/window/window_media_preview.cpp index dcdfd9571..4f0e31cae 100644 --- a/Telegram/SourceFiles/window/window_media_preview.cpp +++ b/Telegram/SourceFiles/window/window_media_preview.cpp @@ -285,9 +285,11 @@ QPixmap MediaPreviewWidget::currentImage() const { ? _gif : _gifThumbnail; if (gif && gif->started()) { - auto s = currentDimensions(); - auto paused = _controller->isGifPausedAtLeastFor(Window::GifPauseReason::MediaPreview); - return gif->current(s.width(), s.height(), s.width(), s.height(), ImageRoundRadius::None, RectPart::None, paused ? 0 : crl::now()); + const auto paused = _controller->isGifPausedAtLeastFor( + Window::GifPauseReason::MediaPreview); + return gif->current( + { .frame = currentDimensions() }, + paused ? 0 : crl::now()); } if (_cacheStatus != CacheThumbLoaded && _document->hasThumbnail()) { @@ -335,14 +337,7 @@ QPixmap MediaPreviewWidget::currentImage() const { void MediaPreviewWidget::startGifAnimation( const Media::Clip::ReaderPointer &gif) { - const auto s = currentDimensions(); - gif->start( - s.width(), - s.height(), - s.width(), - s.height(), - ImageRoundRadius::None, - RectPart::None); + gif->start({ .frame = currentDimensions() }); } void MediaPreviewWidget::validateGifAnimation() {