Move Webm sticker to UnwrappedMedia.

This commit is contained in:
John Preston 2022-08-04 13:35:08 +03:00
parent 5b0d023a88
commit f8e22210e7
33 changed files with 458 additions and 300 deletions

View file

@ -1283,7 +1283,7 @@ void StickerSetBox::Inner::paintSticker(
_lottiePlayer->unpause(element.lottie); _lottiePlayer->unpause(element.lottie);
} else if (element.webm && element.webm->started()) { } else if (element.webm && element.webm->started()) {
p.drawPixmap(ppos, element.webm->current({ p.drawImage(ppos, element.webm->current({
.frame = size, .frame = size,
.keepAlpha = true, .keepAlpha = true,
}, paused ? 0 : now)); }, paused ? 0 : now));

View file

@ -1421,10 +1421,9 @@ void StickersBox::Inner::paintRowThumbnail(
row->lottie->markFrameShown(); row->lottie->markFrameShown();
} }
} else if (row->webm && row->webm->started()) { } else if (row->webm && row->webm->started()) {
p.drawPixmapLeft( p.drawImage(
x, x,
y, y,
width(),
row->webm->current( row->webm->current(
{ .frame = { row->pixw, row->pixh }, .keepAlpha = true }, { .frame = { row->pixw, row->pixh }, .keepAlpha = true },
paused ? 0 : crl::now())); paused ? 0 : crl::now()));

View file

@ -882,7 +882,7 @@ void FieldAutocomplete::Inner::paintEvent(QPaintEvent *e) {
sticker.lottie->markFrameShown(); sticker.lottie->markFrameShown();
} }
} else if (sticker.webm && sticker.webm->started()) { } else if (sticker.webm && sticker.webm->started()) {
p.drawPixmap(ppos, sticker.webm->current({ p.drawImage(ppos, sticker.webm->current({
.frame = size, .frame = size,
.keepAlpha = true, .keepAlpha = true,
}, paused ? 0 : now)); }, paused ? 0 : now));

View file

@ -208,7 +208,7 @@ void StickersListFooter::clearHeavyData() {
icon.lifetime.destroy(); icon.lifetime.destroy();
icon.stickerMedia = nullptr; icon.stickerMedia = nullptr;
if (!info.visible) { if (!info.visible) {
icon.savedFrame = QPixmap(); icon.savedFrame = QImage();
} }
return true; return true;
}); });
@ -1142,7 +1142,7 @@ void StickersListFooter::paintSetIcon(
const auto frame = icon.lottie->frame(); const auto frame = icon.lottie->frame();
const auto size = frame.size() / cIntRetinaFactor(); const auto size = frame.size() / cIntRetinaFactor();
if (icon.savedFrame.isNull()) { if (icon.savedFrame.isNull()) {
icon.savedFrame = QPixmap::fromImage(frame, Qt::ColorOnly); icon.savedFrame = frame;
icon.savedFrame.setDevicePixelRatio(cRetinaFactor()); icon.savedFrame.setDevicePixelRatio(cRetinaFactor());
} }
p.drawImage( p.drawImage(
@ -1163,17 +1163,17 @@ void StickersListFooter::paintSetIcon(
icon.savedFrame = frame; icon.savedFrame = frame;
icon.savedFrame.setDevicePixelRatio(cRetinaFactor()); icon.savedFrame.setDevicePixelRatio(cRetinaFactor());
} }
p.drawPixmapLeft(x, y, width(), frame); p.drawImage(x, y, frame);
} else if (!icon.savedFrame.isNull() || thumb) { } else if (!icon.savedFrame.isNull()) {
const auto pixmap = !icon.savedFrame.isNull() p.drawImage(x, y, icon.savedFrame);
? icon.savedFrame } else if (thumb) {
: (!icon.lottie && thumb) const auto pixmap = (!icon.lottie && thumb)
? thumb->pix(icon.pixw, icon.pixh) ? thumb->pix(icon.pixw, icon.pixh)
: QPixmap(); : QPixmap();
if (pixmap.isNull()) { if (pixmap.isNull()) {
return; return;
} else if (icon.savedFrame.isNull()) { } else if (icon.savedFrame.isNull()) {
icon.savedFrame = pixmap; icon.savedFrame = pixmap.toImage();
} }
p.drawPixmapLeft(x, y, width(), pixmap); p.drawPixmapLeft(x, y, width(), pixmap);
} }

View file

@ -63,7 +63,7 @@ struct StickerIcon {
Data::StickersSet *set = nullptr; Data::StickersSet *set = nullptr;
mutable std::unique_ptr<Lottie::SinglePlayer> lottie; mutable std::unique_ptr<Lottie::SinglePlayer> lottie;
mutable Media::Clip::ReaderPointer webm; mutable Media::Clip::ReaderPointer webm;
mutable QPixmap savedFrame; mutable QImage savedFrame;
DocumentData *sticker = nullptr; DocumentData *sticker = nullptr;
ChannelData *megagroup = nullptr; ChannelData *megagroup = nullptr;
mutable std::shared_ptr<Data::StickersSetThumbnailView> thumbnailMedia; mutable std::shared_ptr<Data::StickersSetThumbnailView> thumbnailMedia;

View file

@ -76,7 +76,7 @@ struct StickersListWidget::Sticker {
std::shared_ptr<Data::DocumentMedia> documentMedia; std::shared_ptr<Data::DocumentMedia> documentMedia;
Lottie::Animation *lottie = nullptr; Lottie::Animation *lottie = nullptr;
Media::Clip::ReaderPointer webm; Media::Clip::ReaderPointer webm;
QPixmap savedFrame; QImage savedFrame;
QSize savedFrameFor; QSize savedFrameFor;
QImage premiumLock; QImage premiumLock;
@ -983,7 +983,7 @@ void StickersListWidget::clearHeavyIn(Set &set, bool clearSavedFrames) {
const auto lifetime = base::take(set.lottieLifetime); const auto lifetime = base::take(set.lottieLifetime);
for (auto &sticker : set.stickers) { for (auto &sticker : set.stickers) {
if (clearSavedFrames) { if (clearSavedFrames) {
sticker.savedFrame = QPixmap(); sticker.savedFrame = QImage();
sticker.savedFrameFor = QSize(); sticker.savedFrameFor = QSize();
} }
sticker.webm = nullptr; sticker.webm = nullptr;
@ -1314,9 +1314,7 @@ void StickersListWidget::paintSticker(
QRect(ppos, lottieFrame.size() / cIntRetinaFactor()), QRect(ppos, lottieFrame.size() / cIntRetinaFactor()),
lottieFrame); lottieFrame);
if (sticker.savedFrame.isNull()) { if (sticker.savedFrame.isNull()) {
sticker.savedFrame = QPixmap::fromImage( sticker.savedFrame = lottieFrame;
lottieFrame,
Qt::ColorOnly);
sticker.savedFrame.setDevicePixelRatio(cRetinaFactor()); sticker.savedFrame.setDevicePixelRatio(cRetinaFactor());
sticker.savedFrameFor = _singleSize; sticker.savedFrameFor = _singleSize;
} }
@ -1330,20 +1328,22 @@ void StickersListWidget::paintSticker(
sticker.savedFrame.setDevicePixelRatio(cRetinaFactor()); sticker.savedFrame.setDevicePixelRatio(cRetinaFactor());
sticker.savedFrameFor = _singleSize; sticker.savedFrameFor = _singleSize;
} }
p.drawPixmapLeft(ppos, width(), frame); p.drawImage(ppos, frame);
} else { } else {
const auto image = media->getStickerSmall(); const auto image = media->getStickerSmall();
const auto useSavedFrame = !sticker.savedFrame.isNull() const auto useSavedFrame = !sticker.savedFrame.isNull()
&& (sticker.savedFrameFor == _singleSize); && (sticker.savedFrameFor == _singleSize);
const auto pixmap = useSavedFrame if (useSavedFrame) {
? sticker.savedFrame p.drawImage(ppos, sticker.savedFrame);
: image if (premium) {
? image->pixSingle(size, { .outer = size }) lottieFrame = sticker.savedFrame;
: QPixmap(); }
if (!pixmap.isNull()) { } else if (image) {
const auto pixmap = image->pixSingle(size, { .outer = size });
p.drawPixmapLeft(ppos, width(), pixmap); p.drawPixmapLeft(ppos, width(), pixmap);
if (sticker.savedFrame.isNull()) { if (sticker.savedFrame.isNull()) {
sticker.savedFrame = pixmap; sticker.savedFrame = pixmap.toImage().convertToFormat(
QImage::Format_ARGB32_Premultiplied);
sticker.savedFrameFor = _singleSize; sticker.savedFrameFor = _singleSize;
} }
if (premium) { if (premium) {

View file

@ -990,7 +990,7 @@ std::unique_ptr<HistoryView::Media> MediaFile::createView(
not_null<HistoryView::Element*> message, not_null<HistoryView::Element*> message,
not_null<HistoryItem*> realParent, not_null<HistoryItem*> realParent,
HistoryView::Element *replacing) { HistoryView::Element *replacing) {
if (const auto info = _document->sticker(); info && !info->isWebm()) { if (_document->sticker()) {
return std::make_unique<HistoryView::UnwrappedMedia>( return std::make_unique<HistoryView::UnwrappedMedia>(
message, message,
std::make_unique<HistoryView::Sticker>( std::make_unique<HistoryView::Sticker>(
@ -998,9 +998,7 @@ std::unique_ptr<HistoryView::Media> MediaFile::createView(
_document, _document,
_skipPremiumEffect, _skipPremiumEffect,
replacing)); replacing));
} else if (_document->isAnimation() } else if (_document->isAnimation() || _document->isVideoFile()) {
|| _document->isVideoFile()
|| (info && info->isWebm())) {
return std::make_unique<HistoryView::Gif>( return std::make_unique<HistoryView::Gif>(
message, message,
realParent, realParent,

View file

@ -86,7 +86,7 @@ void VideoUserpic::paintLeft(
startReady(); startReady();
const auto now = paused ? crl::time(0) : crl::now(); const auto now = paused ? crl::time(0) : crl::now();
p.drawPixmap(x, y, _video->current(request(size), now)); p.drawImage(x, y, _video->current(request(size), now));
} else { } else {
_peer->paintUserpicLeft(p, view, x, y, w, size); _peer->paintUserpicLeft(p, view, x, y, w, size);
} }

View file

@ -34,7 +34,7 @@ ItemSticker::ItemSticker(
} }
const auto updateThumbnail = [=] { const auto updateThumbnail = [=] {
const auto guard = gsl::finally([&] { const auto guard = gsl::finally([&] {
if (_pixmap.isNull()) { if (_image.isNull()) {
setAspectRatio(1.); setAspectRatio(1.);
} }
}); });
@ -47,8 +47,7 @@ ItemSticker::ItemSticker(
Lottie::Quality::High); Lottie::Quality::High);
_lottie.player->updates( _lottie.player->updates(
) | rpl::start_with_next([=] { ) | rpl::start_with_next([=] {
updatePixmap(Ui::PixmapFromImage( updatePixmap(_lottie.player->frame());
_lottie.player->frame()));
_lottie.player = nullptr; _lottie.player = nullptr;
_lottie.lifetime.destroy(); _lottie.lifetime.destroy();
update(); update();
@ -81,7 +80,7 @@ ItemSticker::ItemSticker(
const auto ratio = style::DevicePixelRatio(); const auto ratio = style::DevicePixelRatio();
auto pixmap = sticker->pixNoCache(sticker->size() * ratio); auto pixmap = sticker->pixNoCache(sticker->size() * ratio);
pixmap.setDevicePixelRatio(ratio); pixmap.setDevicePixelRatio(ratio);
updatePixmap(std::move(pixmap)); updatePixmap(pixmap.toImage());
return true; return true;
}; };
if (!updateThumbnail()) { if (!updateThumbnail()) {
@ -95,15 +94,15 @@ ItemSticker::ItemSticker(
} }
} }
void ItemSticker::updatePixmap(QPixmap &&pixmap) { void ItemSticker::updatePixmap(QImage &&image) {
_pixmap = std::move(pixmap); _image = std::move(image);
if (flipped()) { if (flipped()) {
performFlip(); performFlip();
} else { } else {
update(); update();
} }
if (!_pixmap.isNull()) { if (!_image.isNull()) {
setAspectRatio(_pixmap.height() / float64(_pixmap.width())); setAspectRatio(_image.height() / float64(_image.width()));
} }
} }
@ -111,7 +110,7 @@ void ItemSticker::paint(
QPainter *p, QPainter *p,
const QStyleOptionGraphicsItem *option, const QStyleOptionGraphicsItem *option,
QWidget *w) { QWidget *w) {
p->drawPixmap(contentRect().toRect(), _pixmap); p->drawImage(contentRect().toRect(), _image);
ItemBase::paint(p, option, w); ItemBase::paint(p, option, w);
} }
@ -124,7 +123,7 @@ int ItemSticker::type() const {
} }
void ItemSticker::performFlip() { void ItemSticker::performFlip() {
_pixmap = _pixmap.transformed(QTransform().scale(-1, 1)); _image = _image.transformed(QTransform().scale(-1, 1));
update(); update();
} }

View file

@ -42,14 +42,14 @@ private:
const not_null<DocumentData*> _document; const not_null<DocumentData*> _document;
const std::shared_ptr<::Data::DocumentMedia> _mediaView; const std::shared_ptr<::Data::DocumentMedia> _mediaView;
void updatePixmap(QPixmap &&pixmap); void updatePixmap(QImage &&image);
struct { struct {
std::unique_ptr<Lottie::SinglePlayer> player; std::unique_ptr<Lottie::SinglePlayer> player;
rpl::lifetime lifetime; rpl::lifetime lifetime;
} _lottie; } _lottie;
::Media::Clip::ReaderPointer _webm; ::Media::Clip::ReaderPointer _webm;
QPixmap _pixmap; QImage _image;
rpl::lifetime _loadingLifetime; rpl::lifetime _loadingLifetime;

View file

@ -78,7 +78,9 @@ void Dice::draw(Painter &p, const PaintContext &context, const QRect &r) {
_end->draw(p, context, r); _end->draw(p, context, r);
} else if (_start) { } else if (_start) {
_start->draw(p, context, r); _start->draw(p, context, r);
if (_end && _end->readyToDrawLottie() && _start->atTheEnd()) { if (_end
&& _end->readyToDrawAnimationFrame()
&& _start->atTheEnd()) {
_drawingEnd = true; _drawingEnd = true;
} }
} }

View file

@ -24,7 +24,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "history/view/history_view_element.h" #include "history/view/history_view_element.h"
#include "history/view/history_view_cursor_state.h" #include "history/view/history_view_cursor_state.h"
#include "history/view/media/history_view_media_common.h" #include "history/view/media/history_view_media_common.h"
#include "history/view/media/history_view_sticker.h"
#include "window/window_session_controller.h" #include "window/window_session_controller.h"
#include "core/application.h" // Application::showDocument. #include "core/application.h" // Application::showDocument.
#include "ui/chat/chat_style.h" #include "ui/chat/chat_style.h"
@ -39,7 +38,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "data/data_file_click_handler.h" #include "data/data_file_click_handler.h"
#include "data/data_file_origin.h" #include "data/data_file_origin.h"
#include "data/data_document_media.h" #include "data/data_document_media.h"
#include "chat_helpers/stickers_lottie.h" // PaintStickerThumbnailPath.
#include "styles/style_chat.h" #include "styles/style_chat.h"
namespace HistoryView { namespace HistoryView {
@ -81,12 +79,7 @@ Gif::Gif(
, _data(document) , _data(document)
, _caption(st::minPhotoSize - st::msgPadding.left() - st::msgPadding.right()) , _caption(st::minPhotoSize - st::msgPadding.left() - st::msgPadding.right())
, _downloadSize(Ui::FormatSizeText(_data->size)) { , _downloadSize(Ui::FormatSizeText(_data->size)) {
if (const auto info = _data->sticker(); info && info->set) { setDocumentLinks(_data, realParent);
_stickerLink = Sticker::ShowSetHandler(_data);
} else {
setDocumentLinks(_data, realParent);
}
setStatusSize(Ui::FileStatusSizeReady); setStatusSize(Ui::FileStatusSizeReady);
refreshCaption(); refreshCaption();
@ -132,17 +125,13 @@ QSize Gif::sizeForAspectRatio() const {
} }
QSize Gif::countThumbSize(int &inOutWidthMax) const { QSize Gif::countThumbSize(int &inOutWidthMax) const {
const auto maxSize = _data->sticker() const auto maxSize = _data->isVideoFile()
? Sticker::Size().width()
: _data->isVideoFile()
? st::maxMediaSize ? st::maxMediaSize
: _data->isVideoMessage() : _data->isVideoMessage()
? st::maxVideoMessageSize ? st::maxVideoMessageSize
: st::maxGifSize; : st::maxGifSize;
const auto useMaxSize = std::max(maxSize, st::minPhotoSize); const auto useMaxSize = std::max(maxSize, st::minPhotoSize);
const auto size = _data->sticker() const auto size = style::ConvertScale(videoSize());
? videoSize()
: style::ConvertScale(videoSize());
accumulate_min(inOutWidthMax, useMaxSize); accumulate_min(inOutWidthMax, useMaxSize);
return DownscaledSize(size, { inOutWidthMax, useMaxSize }); return DownscaledSize(size, { inOutWidthMax, useMaxSize });
} }
@ -275,9 +264,7 @@ void Gif::draw(Painter &p, const PaintContext &context) const {
ensureDataMediaCreated(); ensureDataMediaCreated();
const auto item = _parent->data(); const auto item = _parent->data();
const auto loaded = dataLoaded(); const auto loaded = dataLoaded();
const auto sticker = _data->sticker(); const auto displayLoading = (item->isSending() || _data->displayLoading());
const auto displayLoading = !sticker
&& (item->isSending() || _data->displayLoading());
const auto st = context.st; const auto st = context.st;
const auto sti = context.imageStyle(); const auto sti = context.imageStyle();
const auto stm = context.messageStyle(); const auto stm = context.messageStyle();
@ -330,9 +317,7 @@ void Gif::draw(Painter &p, const PaintContext &context) const {
} }
updateStatusText(); updateStatusText();
const auto radial = isRadialAnimation() const auto radial = isRadialAnimation()
|| (!sticker || (streamedForWaiting && streamedForWaiting->waitingShown());
&& streamedForWaiting
&& streamedForWaiting->waitingShown());
if (bubble) { if (bubble) {
if (!_caption.isEmpty()) { if (!_caption.isEmpty()) {
@ -398,10 +383,6 @@ void Gif::draw(Painter &p, const PaintContext &context) const {
auto request = ::Media::Streaming::FrameRequest(); auto request = ::Media::Streaming::FrameRequest();
request.outer = QSize(usew, painth) * cIntRetinaFactor(); request.outer = QSize(usew, painth) * cIntRetinaFactor();
request.resize = QSize(_thumbw, _thumbh) * cIntRetinaFactor(); request.resize = QSize(_thumbw, _thumbh) * cIntRetinaFactor();
request.keepAlpha = (sticker != nullptr);
if (sticker && context.selected()) {
request.colored = context.st->msgStickerOverlay()->c;
}
request.corners = roundCorners; request.corners = roundCorners;
request.radius = roundRadius; request.radius = roundRadius;
if (!activeRoundPlaying && activeOwnPlaying->instance.playerLocked()) { if (!activeRoundPlaying && activeOwnPlaying->instance.playerLocked()) {
@ -422,19 +403,9 @@ void Gif::draw(Painter &p, const PaintContext &context) const {
} }
const auto frame = streamed->frameWithInfo(request); const auto frame = streamed->frameWithInfo(request);
const auto playOnce = sticker
&& !Core::App().settings().loopAnimatedStickers();
const auto switchToNext = !playOnce
|| (frame.index != 0)
|| !_stickerOncePlayed;
p.drawImage(rthumb, frame.image); p.drawImage(rthumb, frame.image);
if (!paused if (!paused) {
&& switchToNext streamed->markFrameShown();
&& streamed->markFrameShown()
&& playOnce
&& !_stickerOncePlayed) {
_stickerOncePlayed = true;
_parent->delegate()->elementStartStickerLoop(_parent);
} }
} }
@ -464,13 +435,8 @@ void Gif::draw(Painter &p, const PaintContext &context) const {
ensureDataMediaCreated(); ensureDataMediaCreated();
const auto size = QSize(_thumbw, _thumbh); const auto size = QSize(_thumbw, _thumbh);
const auto args = Images::PrepareArgs{ const auto args = Images::PrepareArgs{
.colored = ((sticker && context.selected()) .options = Images::RoundOptions(roundRadius, roundCorners),
? &context.st->msgStickerOverlay() .outer = QSize(usew, painth),
: nullptr),
.options = (sticker
? Images::Option::TransparentBackground
: Images::RoundOptions(roundRadius, roundCorners)),
.outer = sticker ? QSize() : QSize(usew, painth),
}; };
if (const auto good = _dataMedia->goodThumbnail()) { if (const auto good = _dataMedia->goodThumbnail()) {
p.drawPixmap(rthumb.topLeft(), good->pixSingle(size, args)); p.drawPixmap(rthumb.topLeft(), good->pixSingle(size, args));
@ -507,20 +473,17 @@ void Gif::draw(Painter &p, const PaintContext &context) const {
| (roundBottom ? RectPart::Bottom : RectPart::None); | (roundBottom ? RectPart::Bottom : RectPart::None);
Ui::FillRoundRect(p, rthumb.marginsAdded({ 0, roundTop ? 0 : margin, 0, roundBottom ? 0 : margin }), st->imageBg(), roundRadius, parts); Ui::FillRoundRect(p, rthumb.marginsAdded({ 0, roundTop ? 0 : margin, 0, roundBottom ? 0 : margin }), st->imageBg(), roundRadius, parts);
} }
} else {
paintPath(p, context, rthumb);
} }
} }
} }
} }
if (context.selected() && !sticker) { if (context.selected()) {
Ui::FillComplexOverlayRect(p, st, rthumb, roundRadius, roundCorners); Ui::FillComplexOverlayRect(p, st, rthumb, roundRadius, roundCorners);
} }
if (radial if (radial
|| (!sticker || (!streamingMode
&& !streamingMode
&& ((!loaded && !_data->loading()) || !autoplay))) { && ((!loaded && !_data->loading()) || !autoplay))) {
const auto radialOpacity = (item->isSending() || _data->uploading()) const auto radialOpacity = (item->isSending() || _data->uploading())
? 1. ? 1.
@ -725,28 +688,6 @@ void Gif::validateVideoThumbnail() const {
: info.thumbnail); : info.thumbnail);
} }
void Gif::paintPath(
Painter &p,
const PaintContext &context,
const QRect &r) const {
Expects(_dataMedia != nullptr);
const auto pathGradient = _parent->delegate()->elementPathShiftGradient();
if (context.selected()) {
pathGradient->overrideColors(
context.st->msgServiceBgSelected(),
context.st->msgServiceBg());
} else {
pathGradient->clearOverridenColors();
}
p.setBrush(context.imageStyle()->msgServiceBg);
ChatHelpers::PaintStickerThumbnailPath(
p,
_dataMedia.get(),
r,
pathGradient);
}
void Gif::drawCornerStatus( void Gif::drawCornerStatus(
Painter &p, Painter &p,
const PaintContext &context, const PaintContext &context,
@ -923,9 +864,7 @@ TextState Gif::textState(QPoint point, StateRequest request) const {
} }
if (QRect(usex + paintx, painty, usew, painth).contains(point)) { if (QRect(usex + paintx, painty, usew, painth).contains(point)) {
ensureDataMediaCreated(); ensureDataMediaCreated();
result.link = _data->sticker() result.link = _data->uploading()
? _stickerLink
: _data->uploading()
? _cancell ? _cancell
: _realParent->isSending() : _realParent->isSending()
? nullptr ? nullptr
@ -1241,7 +1180,7 @@ bool Gif::uploading() const {
} }
bool Gif::needsBubble() const { bool Gif::needsBubble() const {
if (_data->sticker() || _data->isVideoMessage()) { if (_data->isVideoMessage()) {
return false; return false;
} else if (!_caption.isEmpty()) { } else if (!_caption.isEmpty()) {
return true; return true;
@ -1333,8 +1272,7 @@ int Gif::additionalWidth() const {
} }
bool Gif::isUnwrapped() const { bool Gif::isUnwrapped() const {
return (_data->sticker() || _data->isVideoMessage()) return _data->isVideoMessage() && (_parent->media() == this);
&& (_parent->media() == this);
} }
void Gif::validateGroupedCache( void Gif::validateGroupedCache(
@ -1684,7 +1622,6 @@ bool Gif::needInfoDisplay() const {
return _parent->data()->isSending() return _parent->data()->isSending()
|| _data->uploading() || _data->uploading()
|| _parent->isUnderCursor() || _parent->isUnderCursor()
|| (_data->sticker() && _parent->rightActionSize())
// Don't show the GIF badge if this message has text. // Don't show the GIF badge if this message has text.
|| (!_parent->hasBubble() && _parent->isLastAndSelfMessage()); || (!_parent->hasBubble() && _parent->isLastAndSelfMessage());
} }

View file

@ -101,10 +101,6 @@ public:
QPoint resolveCustomInfoRightBottom() const override; QPoint resolveCustomInfoRightBottom() const override;
QString additionalInfoString() const override; QString additionalInfoString() const override;
void stickerClearLoopPlayed() override {
_stickerOncePlayed = false;
}
bool skipBubbleTail() const override { bool skipBubbleTail() const override {
return isRoundedInBubbleBottom() && _caption.isEmpty(); return isRoundedInBubbleBottom() && _caption.isEmpty();
} }
@ -181,11 +177,6 @@ private:
StateRequest request, StateRequest request,
QPoint position) const; QPoint position) const;
void paintPath(
Painter &p,
const PaintContext &context,
const QRect &r) const;
const not_null<DocumentData*> _data; const not_null<DocumentData*> _data;
int _thumbw = 1; int _thumbw = 1;
int _thumbh = 1; int _thumbh = 1;
@ -193,10 +184,7 @@ private:
std::unique_ptr<Streamed> _streamed; std::unique_ptr<Streamed> _streamed;
mutable std::shared_ptr<Data::DocumentMedia> _dataMedia; mutable std::shared_ptr<Data::DocumentMedia> _dataMedia;
mutable std::unique_ptr<Image> _videoThumbnailFrame; mutable std::unique_ptr<Image> _videoThumbnailFrame;
ClickHandlerPtr _stickerLink;
QString _downloadSize; QString _downloadSize;
mutable bool _stickerOncePlayed = false;
}; };

View file

@ -12,7 +12,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "history/view/history_view_element.h" #include "history/view/history_view_element.h"
#include "history/view/history_view_cursor_state.h" #include "history/view/history_view_cursor_state.h"
#include "history/view/history_view_spoiler_click_handler.h" #include "history/view/history_view_spoiler_click_handler.h"
#include "lottie/lottie_single_player.h" #include "history/view/media/history_view_sticker.h"
#include "storage/storage_shared_media.h" #include "storage/storage_shared_media.h"
#include "data/data_document.h" #include "data/data_document.h"
#include "data/data_session.h" #include "data/data_session.h"
@ -243,7 +243,7 @@ PointState Media::pointState(QPoint point) const {
: PointState::Outside; : PointState::Outside;
} }
std::unique_ptr<Lottie::SinglePlayer> Media::stickerTakeLottie( std::unique_ptr<StickerPlayer> Media::stickerTakePlayer(
not_null<DocumentData*> data, not_null<DocumentData*> data,
const Lottie::ColorReplacements *replacements) { const Lottie::ColorReplacements *replacements) {
return nullptr; return nullptr;

View file

@ -25,7 +25,6 @@ using SharedMediaTypesMask = base::enum_mask<SharedMediaType>;
} // namespace Storage } // namespace Storage
namespace Lottie { namespace Lottie {
class SinglePlayer;
struct ColorReplacements; struct ColorReplacements;
} // namespace Lottie } // namespace Lottie
@ -41,6 +40,7 @@ enum class CursorState : char;
enum class InfoDisplayType : char; enum class InfoDisplayType : char;
struct TextState; struct TextState;
struct StateRequest; struct StateRequest;
class StickerPlayer;
class Element; class Element;
using PaintContext = Ui::ChatPaintContext; using PaintContext = Ui::ChatPaintContext;
@ -172,7 +172,7 @@ public:
} }
virtual void stickerClearLoopPlayed() { virtual void stickerClearLoopPlayed() {
} }
virtual std::unique_ptr<Lottie::SinglePlayer> stickerTakeLottie( virtual std::unique_ptr<StickerPlayer> stickerTakePlayer(
not_null<DocumentData*> data, not_null<DocumentData*> data,
const Lottie::ColorReplacements *replacements); const Lottie::ColorReplacements *replacements);
virtual void checkAnimation() { virtual void checkAnimation() {

View file

@ -25,10 +25,9 @@ constexpr auto kMaxForwardedBarLines = 4;
} // namespace } // namespace
auto UnwrappedMedia::Content::stickerTakeLottie( std::unique_ptr<StickerPlayer> UnwrappedMedia::Content::stickerTakePlayer(
not_null<DocumentData*> data, not_null<DocumentData*> data,
const Lottie::ColorReplacements *replacements) const Lottie::ColorReplacements *replacements) {
-> std::unique_ptr<Lottie::SinglePlayer> {
return nullptr; return nullptr;
} }
@ -465,10 +464,10 @@ QPoint UnwrappedMedia::resolveCustomInfoRightBottom() const {
return QPoint(fullRight - skipx, fullBottom - skipy); return QPoint(fullRight - skipx, fullBottom - skipy);
} }
std::unique_ptr<Lottie::SinglePlayer> UnwrappedMedia::stickerTakeLottie( std::unique_ptr<StickerPlayer> UnwrappedMedia::stickerTakePlayer(
not_null<DocumentData*> data, not_null<DocumentData*> data,
const Lottie::ColorReplacements *replacements) { const Lottie::ColorReplacements *replacements) {
return _content->stickerTakeLottie(data, replacements); return _content->stickerTakePlayer(data, replacements);
} }
//void UnwrappedMedia::externalLottieProgressing(bool external) { //void UnwrappedMedia::externalLottieProgressing(bool external) {

View file

@ -38,7 +38,7 @@ public:
} }
virtual void stickerClearLoopPlayed() { virtual void stickerClearLoopPlayed() {
} }
virtual std::unique_ptr<Lottie::SinglePlayer> stickerTakeLottie( virtual std::unique_ptr<StickerPlayer> stickerTakePlayer(
not_null<DocumentData*> data, not_null<DocumentData*> data,
const Lottie::ColorReplacements *replacements); const Lottie::ColorReplacements *replacements);
@ -104,7 +104,7 @@ public:
void stickerClearLoopPlayed() override { void stickerClearLoopPlayed() override {
_content->stickerClearLoopPlayed(); _content->stickerClearLoopPlayed();
} }
std::unique_ptr<Lottie::SinglePlayer> stickerTakeLottie( std::unique_ptr<StickerPlayer> stickerTakePlayer(
not_null<DocumentData*> data, not_null<DocumentData*> data,
const Lottie::ColorReplacements *replacements) override; const Lottie::ColorReplacements *replacements) override;

View file

@ -195,11 +195,11 @@ void MediaGift::stickerClearLoopPlayed() {
} }
} }
std::unique_ptr<Lottie::SinglePlayer> MediaGift::stickerTakeLottie( std::unique_ptr<StickerPlayer> MediaGift::stickerTakePlayer(
not_null<DocumentData*> data, not_null<DocumentData*> data,
const Lottie::ColorReplacements *replacements) { const Lottie::ColorReplacements *replacements) {
return _sticker return _sticker
? _sticker->stickerTakeLottie(data, replacements) ? _sticker->stickerTakePlayer(data, replacements)
: nullptr; : nullptr;
} }

View file

@ -42,7 +42,7 @@ public:
bool pressed) override; bool pressed) override;
void stickerClearLoopPlayed() override; void stickerClearLoopPlayed() override;
std::unique_ptr<Lottie::SinglePlayer> stickerTakeLottie( std::unique_ptr<StickerPlayer> stickerTakePlayer(
not_null<DocumentData*> data, not_null<DocumentData*> data,
const Lottie::ColorReplacements *replacements) override; const Lottie::ColorReplacements *replacements) override;

View file

@ -161,16 +161,16 @@ void SlotMachine::draw(
// } // }
//} //}
auto switchedToEnd = _drawingEnd; auto switchedToEnd = _drawingEnd;
const auto pullReady = _pull && _pull->readyToDrawLottie(); const auto pullReady = _pull && _pull->readyToDrawAnimationFrame();
const auto paintReady = [&] { const auto paintReady = [&] {
auto result = pullReady; auto result = pullReady;
auto allPlayedEnough = true; auto allPlayedEnough = true;
for (auto i = 1; i != 4; ++i) { for (auto i = 1; i != 4; ++i) {
if (!_end[i] || !_end[i]->readyToDrawLottie()) { if (!_end[i] || !_end[i]->readyToDrawAnimationFrame()) {
switchedToEnd[i] = false; switchedToEnd[i] = false;
} }
if (!switchedToEnd[i] if (!switchedToEnd[i]
&& (!_start[i] || !_start[i]->readyToDrawLottie())) { && (!_start[i] || !_start[i]->readyToDrawAnimationFrame())) {
result = false; result = false;
} }
const auto playedTillFrame = !switchedToEnd[i] const auto playedTillFrame = !switchedToEnd[i]
@ -180,11 +180,13 @@ void SlotMachine::draw(
allPlayedEnough = false; allPlayedEnough = false;
} }
} }
if (!_end[0] || !_end[0]->readyToDrawLottie() || !allPlayedEnough) { if (!_end[0]
|| !_end[0]->readyToDrawAnimationFrame()
|| !allPlayedEnough) {
switchedToEnd[0] = false; switchedToEnd[0] = false;
} }
if (ranges::contains(switchedToEnd, false) if (ranges::contains(switchedToEnd, false)
&& (!_start[0] || !_start[0]->readyToDrawLottie())) { && (!_start[0] || !_start[0]->readyToDrawAnimationFrame())) {
result = false; result = false;
} }
return result; return result;
@ -200,7 +202,7 @@ void SlotMachine::draw(
} else { } else {
_start[i]->draw(p, context, r); _start[i]->draw(p, context, r);
if (_end[i] if (_end[i]
&& _end[i]->readyToDrawLottie() && _end[i]->readyToDrawAnimationFrame()
&& _start[i]->atTheEnd()) { && _start[i]->atTheEnd()) {
_drawingEnd[i] = true; _drawingEnd[i] = true;
} }

View file

@ -31,6 +31,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "data/data_file_click_handler.h" #include "data/data_file_click_handler.h"
#include "data/data_file_origin.h" #include "data/data_file_origin.h"
#include "lottie/lottie_single_player.h" #include "lottie/lottie_single_player.h"
#include "media/clip/media_clip_reader.h"
#include "chat_helpers/stickers_gift_box_pack.h" #include "chat_helpers/stickers_gift_box_pack.h"
#include "chat_helpers/stickers_lottie.h" #include "chat_helpers/stickers_lottie.h"
#include "styles/style_chat.h" #include "styles/style_chat.h"
@ -43,6 +44,8 @@ constexpr auto kMaxEmojiSizeFixed = 256;
constexpr auto kPremiumMultiplier = (1 + 0.245 * 2); constexpr auto kPremiumMultiplier = (1 + 0.245 * 2);
constexpr auto kEmojiMultiplier = 3; constexpr auto kEmojiMultiplier = 3;
using ClipNotification = ::Media::Clip::Notification;
[[nodiscard]] QImage CacheDiceImage( [[nodiscard]] QImage CacheDiceImage(
const QString &emoji, const QString &emoji,
int index, int index,
@ -57,6 +60,154 @@ constexpr auto kEmojiMultiplier = 3;
return image; return image;
} }
class LottiePlayer final : public StickerPlayer {
public:
explicit LottiePlayer(std::unique_ptr<Lottie::SinglePlayer> lottie);
void setRepaintCallback(Fn<void()> callback) override;
bool ready() override;
int framesCount() override;
FrameInfo frame(
QSize size,
QColor colored,
bool mirrorHorizontal,
crl::time now,
bool paused) override;
bool markFrameShown() override;
private:
std::unique_ptr<Lottie::SinglePlayer> _lottie;
rpl::lifetime _repaintLifetime;
};
LottiePlayer::LottiePlayer(std::unique_ptr<Lottie::SinglePlayer> lottie)
: _lottie(std::move(lottie)) {
}
void LottiePlayer::setRepaintCallback(Fn<void()> callback) {
_repaintLifetime = _lottie->updates(
) | rpl::start_with_next([=](Lottie::Update update) {
v::match(update.data, [&](const Lottie::Information &information) {
callback();
//markFramesTillExternal();
}, [&](const Lottie::DisplayFrameRequest &request) {
callback();
});
});
}
bool LottiePlayer::ready() {
return _lottie->ready();
}
int LottiePlayer::framesCount() {
return _lottie->information().framesCount;
}
LottiePlayer::FrameInfo LottiePlayer::frame(
QSize size,
QColor colored,
bool mirrorHorizontal,
crl::time now,
bool paused) {
auto request = Lottie::FrameRequest();
request.box = size * style::DevicePixelRatio();
request.colored = colored;
request.mirrorHorizontal = mirrorHorizontal;
const auto info = _lottie->frameInfo(request);
return { .image = info.image, .index = info.index };
}
bool LottiePlayer::markFrameShown() {
return _lottie->markFrameShown();
}
class WebmPlayer final : public StickerPlayer {
public:
WebmPlayer(
const Core::FileLocation &location,
const QByteArray &data,
QSize size);
void setRepaintCallback(Fn<void()> callback) override;
bool ready() override;
int framesCount() override;
FrameInfo frame(
QSize size,
QColor colored,
bool mirrorHorizontal,
crl::time now,
bool paused) override;
bool markFrameShown() override;
private:
void clipCallback(ClipNotification notification);
::Media::Clip::ReaderPointer _reader;
Fn<void()> _repaintCallback;
QSize _size;
};
WebmPlayer::WebmPlayer(
const Core::FileLocation &location,
const QByteArray &data,
QSize size)
: _reader(
::Media::Clip::MakeReader(location, data, [=](ClipNotification update) {
clipCallback(update);
}))
, _size(size) {
}
void WebmPlayer::clipCallback(ClipNotification notification) {
switch (notification) {
case ClipNotification::Reinit: {
if (_reader->state() == ::Media::Clip::State::Error) {
_reader.setBad();
} else if (_reader->ready() && !_reader->started()) {
_reader->start({ .frame = _size, .keepAlpha = true });
}
} break;
case ClipNotification::Repaint: break;
}
_repaintCallback();
}
void WebmPlayer::setRepaintCallback(Fn<void()> callback) {
_repaintCallback = std::move(callback);
}
bool WebmPlayer::ready() {
return _reader && _reader->started();
}
int WebmPlayer::framesCount() {
return -1;
}
WebmPlayer::FrameInfo WebmPlayer::frame(
QSize size,
QColor colored,
bool mirrorHorizontal,
crl::time now,
bool paused) {
auto request = ::Media::Clip::FrameRequest();
request.frame = size;
request.factor = style::DevicePixelRatio();
request.keepAlpha = true;
request.colored = colored;
const auto info = _reader->frameInfo(request, paused ? 0 : now);
return { .image = info.image, .index = info.index };
}
bool WebmPlayer::markFrameShown() {
return _reader->moveToNextFrame();
}
} // namespace } // namespace
Sticker::Sticker( Sticker::Sticker(
@ -69,7 +220,7 @@ Sticker::Sticker(
, _data(data) , _data(data)
, _replacements(replacements) , _replacements(replacements)
, _cachingTag(ChatHelpers::StickerLottieSize::MessageHistory) , _cachingTag(ChatHelpers::StickerLottieSize::MessageHistory)
, _lottieOncePlayed(false) , _oncePlayed(false)
, _premiumEffectPlayed(false) , _premiumEffectPlayed(false)
, _nextLastDiceFrame(false) , _nextLastDiceFrame(false)
, _skipPremiumEffect(skipPremiumEffect) { , _skipPremiumEffect(skipPremiumEffect) {
@ -82,22 +233,22 @@ Sticker::Sticker(
} }
} }
if (const auto media = replacing ? replacing->media() : nullptr) { if (const auto media = replacing ? replacing->media() : nullptr) {
_lottie = media->stickerTakeLottie(_data, _replacements); _player = media->stickerTakePlayer(_data, _replacements);
if (_lottie) { if (_player) {
//_externalInfo = media->externalLottieInfo(); //_externalInfo = media->externalLottieInfo();
if (hasPremiumEffect() && !_premiumEffectPlayed) { if (hasPremiumEffect() && !_premiumEffectPlayed) {
_premiumEffectPlayed = true; _premiumEffectPlayed = true;
_parent->delegate()->elementStartPremium(_parent, replacing); _parent->delegate()->elementStartPremium(_parent, replacing);
} }
lottieCreated(); playerCreated();
} }
} }
} }
Sticker::~Sticker() { Sticker::~Sticker() {
if (_lottie || _dataMedia) { if (_player || _dataMedia) {
if (_lottie) { if (_player) {
unloadLottie(); unloadPlayer();
} }
if (_dataMedia) { if (_dataMedia) {
_data->owner().keepAlive(base::take(_dataMedia)); _data->owner().keepAlive(base::take(_dataMedia));
@ -127,21 +278,22 @@ void Sticker::initSize() {
_size = EmojiSize(); _size = EmojiSize();
} }
if (_diceIndex > 0) { if (_diceIndex > 0) {
[[maybe_unused]] bool result = readyToDrawLottie(); [[maybe_unused]] bool result = readyToDrawAnimationFrame();
} }
} else { } else {
_size = Size(_data); _size = Size(_data);
} }
_size = DownscaledSize(_size, Size());
} }
QSize Sticker::countOptimalSize() { QSize Sticker::countOptimalSize() {
if (_size.isEmpty()) { if (_size.isEmpty()) {
initSize(); initSize();
} }
return DownscaledSize(_size, Size()); return _size;
} }
bool Sticker::readyToDrawLottie() { bool Sticker::readyToDrawAnimationFrame() {
if (!_lastDiceFrame.isNull()) { if (!_lastDiceFrame.isNull()) {
return true; return true;
} }
@ -155,10 +307,10 @@ bool Sticker::readyToDrawLottie() {
const auto loaded = _dataMedia->loaded(); const auto loaded = _dataMedia->loaded();
const auto waitingForPremium = hasPremiumEffect() const auto waitingForPremium = hasPremiumEffect()
&& _dataMedia->videoThumbnailContent().isEmpty(); && _dataMedia->videoThumbnailContent().isEmpty();
if (sticker->isLottie() && !_lottie && loaded && !waitingForPremium) { if (!_player && loaded && !waitingForPremium && sticker->isAnimated()) {
setupLottie(); setupPlayer();
} }
return (_lottie && _lottie->ready()); return (_player && _player->ready());
} }
QSize Sticker::Size() { QSize Sticker::Size() {
@ -192,13 +344,13 @@ void Sticker::draw(
Painter &p, Painter &p,
const PaintContext &context, const PaintContext &context,
const QRect &r) { const QRect &r) {
if (!customEmojiPart() && isEmojiSticker()) { if (!customEmojiPart()) {
_parent->clearCustomEmojiRepaint(); _parent->clearCustomEmojiRepaint();
} }
ensureDataMediaCreated(); ensureDataMediaCreated();
if (readyToDrawLottie()) { if (readyToDrawAnimationFrame()) {
paintLottie(p, context, r); paintAnimationFrame(p, context, r);
} else if (!_data->sticker() } else if (!_data->sticker()
|| (_data->sticker()->isLottie() && _replacements) || (_data->sticker()->isLottie() && _replacements)
|| !paintPixmap(p, context, r)) { || !paintPixmap(p, context, r)) {
@ -215,23 +367,28 @@ DocumentData *Sticker::document() {
} }
void Sticker::stickerClearLoopPlayed() { void Sticker::stickerClearLoopPlayed() {
_lottieOncePlayed = false; _oncePlayed = false;
_premiumEffectPlayed = false; _premiumEffectPlayed = false;
} }
void Sticker::paintLottie( void Sticker::paintAnimationFrame(
Painter &p, Painter &p,
const PaintContext &context, const PaintContext &context,
const QRect &r) { const QRect &r) {
auto request = Lottie::FrameRequest(); const auto colored = (context.selected() && !_nextLastDiceFrame)
request.box = _size * cIntRetinaFactor(); ? context.st->msgStickerOverlay()->c
if (context.selected() && !_nextLastDiceFrame) { : QColor(0, 0, 0, 0);
request.colored = context.st->msgStickerOverlay()->c; const auto paused = /*(_externalInfo.frame >= 0)
} ? (_frameIndex % _externalInfo.count >= _externalInfo.frame)
request.mirrorHorizontal = mirrorHorizontal(); : */_parent->delegate()->elementIsGifPaused();
const auto frame = _lottie const auto frame = _player
? _lottie->frameInfo(request) ? _player->frame(
: Lottie::Animation::FrameInfo(); _size,
colored,
mirrorHorizontal(),
context.now,
paused)
: StickerPlayer::FrameInfo();
if (_nextLastDiceFrame) { if (_nextLastDiceFrame) {
_nextLastDiceFrame = false; _nextLastDiceFrame = false;
_lastDiceFrame = CacheDiceImage(_diceEmoji, _diceIndex, frame.image); _lastDiceFrame = CacheDiceImage(_diceEmoji, _diceIndex, frame.image);
@ -256,12 +413,9 @@ void Sticker::paintLottie(
return; return;
} }
const auto count = _lottie->information().framesCount; const auto count = _player->framesCount();
_frameIndex = frame.index; _frameIndex = frame.index;
_framesCount = count; _framesCount = count;
const auto paused = /*(_externalInfo.frame >= 0)
? (_frameIndex % _externalInfo.count >= _externalInfo.frame)
: */_parent->delegate()->elementIsGifPaused();
_nextLastDiceFrame = !paused _nextLastDiceFrame = !paused
&& (_diceIndex > 0) && (_diceIndex > 0)
&& (_frameIndex + 2 == count); && (_frameIndex + 2 == count);
@ -274,13 +428,13 @@ void Sticker::paintLottie(
const auto lastDiceFrame = (_diceIndex > 0) && atTheEnd(); const auto lastDiceFrame = (_diceIndex > 0) && atTheEnd();
const auto switchToNext = /*(_externalInfo.frame >= 0) const auto switchToNext = /*(_externalInfo.frame >= 0)
|| */!playOnce || */!playOnce
|| (!lastDiceFrame && (_frameIndex != 0 || !_lottieOncePlayed)); || (!lastDiceFrame && (_frameIndex != 0 || !_oncePlayed));
if (!paused if (!paused
&& switchToNext && switchToNext
&& _lottie->markFrameShown() && _player->markFrameShown()
&& playOnce && playOnce
&& !_lottieOncePlayed) { && !_oncePlayed) {
_lottieOncePlayed = true; _oncePlayed = true;
_parent->delegate()->elementStartStickerLoop(_parent); _parent->delegate()->elementStartStickerLoop(_parent);
} }
checkPremiumEffectStart(); checkPremiumEffectStart();
@ -419,10 +573,10 @@ void Sticker::refreshLink() {
} }
void Sticker::emojiStickerClicked() { void Sticker::emojiStickerClicked() {
if (_lottie) { if (_player) {
_parent->delegate()->elementStartInteraction(_parent); _parent->delegate()->elementStartInteraction(_parent);
} }
_lottieOncePlayed = false; _oncePlayed = false;
_parent->history()->owner().requestViewRepaint(_parent); _parent->history()->owner().requestViewRepaint(_parent);
} }
@ -464,17 +618,26 @@ void Sticker::setCustomEmojiPart(
_cachingTag = tag; _cachingTag = tag;
} }
void Sticker::setupLottie() { void Sticker::setupPlayer() {
Expects(_dataMedia != nullptr); Expects(_dataMedia != nullptr);
_lottie = ChatHelpers::LottiePlayerFromDocument( if (_data->sticker()->isLottie()) {
_dataMedia.get(), _player = std::make_unique<LottiePlayer>(
_replacements, ChatHelpers::LottiePlayerFromDocument(
_cachingTag, _dataMedia.get(),
countOptimalSize() * style::DevicePixelRatio(), _replacements,
Lottie::Quality::High); _cachingTag,
countOptimalSize() * style::DevicePixelRatio(),
Lottie::Quality::High));
} else if (_data->sticker()->isWebm()) {
_player = std::make_unique<WebmPlayer>(
_dataMedia->owner()->location(),
_dataMedia->bytes(),
countOptimalSize());
}
checkPremiumEffectStart(); checkPremiumEffectStart();
lottieCreated(); playerCreated();
} }
void Sticker::checkPremiumEffectStart() { void Sticker::checkPremiumEffectStart() {
@ -484,51 +647,42 @@ void Sticker::checkPremiumEffectStart() {
} }
} }
void Sticker::lottieCreated() { void Sticker::playerCreated() {
Expects(_lottie != nullptr); Expects(_player != nullptr);
_parent->history()->owner().registerHeavyViewPart(_parent); _parent->history()->owner().registerHeavyViewPart(_parent);
_player->setRepaintCallback([=] { _parent->customEmojiRepaint(); });
_lottie->updates(
) | rpl::start_with_next([=](Lottie::Update update) {
v::match(update.data, [&](const Lottie::Information &information) {
_parent->customEmojiRepaint();
//markFramesTillExternal();
}, [&](const Lottie::DisplayFrameRequest &request) {
_parent->customEmojiRepaint();
});
}, _lifetime);
} }
bool Sticker::hasHeavyPart() const { bool Sticker::hasHeavyPart() const {
return _lottie || _dataMedia; return _player || _dataMedia;
} }
void Sticker::unloadHeavyPart() { void Sticker::unloadHeavyPart() {
unloadLottie(); unloadPlayer();
_dataMedia = nullptr; _dataMedia = nullptr;
} }
void Sticker::unloadLottie() { void Sticker::unloadPlayer() {
if (!_lottie) { if (!_player) {
return; return;
} }
if (_diceIndex > 0 && _lastDiceFrame.isNull()) { if (_diceIndex > 0 && _lastDiceFrame.isNull()) {
_nextLastDiceFrame = false; _nextLastDiceFrame = false;
_lottieOncePlayed = false; _oncePlayed = false;
} }
_lottie = nullptr; _player = nullptr;
if (hasPremiumEffect()) { if (hasPremiumEffect()) {
_parent->delegate()->elementCancelPremium(_parent); _parent->delegate()->elementCancelPremium(_parent);
} }
_parent->checkHeavyPart(); _parent->checkHeavyPart();
} }
std::unique_ptr<Lottie::SinglePlayer> Sticker::stickerTakeLottie( std::unique_ptr<StickerPlayer> Sticker::stickerTakePlayer(
not_null<DocumentData*> data, not_null<DocumentData*> data,
const Lottie::ColorReplacements *replacements) { const Lottie::ColorReplacements *replacements) {
return (data == _data && replacements == _replacements) return (data == _data && replacements == _replacements)
? std::move(_lottie) ? std::move(_player)
: nullptr; : nullptr;
} }

View file

@ -30,6 +30,26 @@ enum class StickerLottieSize : uint8;
namespace HistoryView { namespace HistoryView {
class StickerPlayer {
public:
virtual ~StickerPlayer() = default;
struct FrameInfo {
QImage image;
int index = 0;
};
virtual void setRepaintCallback(Fn<void()> callback) = 0;
[[nodiscard]] virtual bool ready() = 0;
[[nodiscard]] virtual int framesCount() = 0;
[[nodiscard]] virtual FrameInfo frame(
QSize size,
QColor colored,
bool mirrorHorizontal,
crl::time now,
bool paused) = 0;
virtual bool markFrameShown() = 0;
};
class Sticker final class Sticker final
: public UnwrappedMedia::Content : public UnwrappedMedia::Content
, public base::has_weak_ptr { , public base::has_weak_ptr {
@ -52,7 +72,7 @@ public:
DocumentData *document() override; DocumentData *document() override;
void stickerClearLoopPlayed() override; void stickerClearLoopPlayed() override;
std::unique_ptr<Lottie::SinglePlayer> stickerTakeLottie( std::unique_ptr<StickerPlayer> stickerTakePlayer(
not_null<DocumentData*> data, not_null<DocumentData*> data,
const Lottie::ColorReplacements *replacements) override; const Lottie::ColorReplacements *replacements) override;
@ -83,7 +103,7 @@ public:
? std::make_optional(_framesCount) ? std::make_optional(_framesCount)
: std::nullopt; : std::nullopt;
} }
[[nodiscard]] bool readyToDrawLottie(); [[nodiscard]] bool readyToDrawAnimationFrame();
[[nodiscard]] static QSize Size(); [[nodiscard]] static QSize Size();
[[nodiscard]] static QSize Size(not_null<DocumentData*> document); [[nodiscard]] static QSize Size(not_null<DocumentData*> document);
@ -99,7 +119,10 @@ private:
[[nodiscard]] bool hasPremiumEffect() const; [[nodiscard]] bool hasPremiumEffect() const;
[[nodiscard]] bool customEmojiPart() const; [[nodiscard]] bool customEmojiPart() const;
[[nodiscard]] bool isEmojiSticker() const; [[nodiscard]] bool isEmojiSticker() const;
void paintLottie(Painter &p, const PaintContext &context, const QRect &r); void paintAnimationFrame(
Painter &p,
const PaintContext &context,
const QRect &r);
bool paintPixmap(Painter &p, const PaintContext &context, const QRect &r); bool paintPixmap(Painter &p, const PaintContext &context, const QRect &r);
void paintPath(Painter &p, const PaintContext &context, const QRect &r); void paintPath(Painter &p, const PaintContext &context, const QRect &r);
[[nodiscard]] QPixmap paintedPixmap(const PaintContext &context) const; [[nodiscard]] QPixmap paintedPixmap(const PaintContext &context) const;
@ -108,9 +131,9 @@ private:
void ensureDataMediaCreated() const; void ensureDataMediaCreated() const;
void dataMediaCreated() const; void dataMediaCreated() const;
void setupLottie(); void setupPlayer();
void lottieCreated(); void playerCreated();
void unloadLottie(); void unloadPlayer();
void emojiStickerClicked(); void emojiStickerClicked();
void premiumStickerClicked(); void premiumStickerClicked();
void checkPremiumEffectStart(); void checkPremiumEffectStart();
@ -119,7 +142,7 @@ private:
const not_null<Element*> _parent; const not_null<Element*> _parent;
const not_null<DocumentData*> _data; const not_null<DocumentData*> _data;
const Lottie::ColorReplacements *_replacements = nullptr; const Lottie::ColorReplacements *_replacements = nullptr;
std::unique_ptr<Lottie::SinglePlayer> _lottie; std::unique_ptr<StickerPlayer> _player;
mutable std::shared_ptr<Data::DocumentMedia> _dataMedia; mutable std::shared_ptr<Data::DocumentMedia> _dataMedia;
ClickHandlerPtr _link; ClickHandlerPtr _link;
QSize _size; QSize _size;
@ -130,13 +153,11 @@ private:
mutable int _frameIndex = -1; mutable int _frameIndex = -1;
mutable int _framesCount = -1; mutable int _framesCount = -1;
ChatHelpers::StickerLottieSize _cachingTag = {}; ChatHelpers::StickerLottieSize _cachingTag = {};
mutable bool _lottieOncePlayed : 1; mutable bool _oncePlayed : 1;
mutable bool _premiumEffectPlayed : 1; mutable bool _premiumEffectPlayed : 1;
mutable bool _nextLastDiceFrame : 1; mutable bool _nextLastDiceFrame : 1;
bool _skipPremiumEffect : 1; bool _skipPremiumEffect : 1;
rpl::lifetime _lifetime;
}; };
} // namespace HistoryView } // namespace HistoryView

View file

@ -190,13 +190,13 @@ void Gif::paint(Painter &p, const QRect &clip, const PaintContext *context) cons
_thumb = pixmap; _thumb = pixmap;
_thumbGood = true; _thumbGood = true;
} }
p.drawPixmap(r.topLeft(), pixmap); p.drawImage(r.topLeft(), pixmap);
} else { } else {
prepareThumbnail(r.size(), frame); prepareThumbnail(r.size(), frame);
if (_thumb.isNull()) { if (_thumb.isNull()) {
p.fillRect(r, st::overviewPhotoBg); p.fillRect(r, st::overviewPhotoBg);
} else { } else {
p.drawPixmap(r.topLeft(), _thumb); p.drawImage(r.topLeft(), _thumb);
} }
} }
@ -340,7 +340,7 @@ void Gif::validateThumbnail(
.options = (Images::Option::TransparentBackground .options = (Images::Option::TransparentBackground
| (good ? Images::Option() : Images::Option::Blur)), | (good ? Images::Option() : Images::Option::Blur)),
.outer = size, .outer = size,
}); }).toImage();
} }
void Gif::prepareThumbnail(QSize size, QSize frame) const { void Gif::prepareThumbnail(QSize size, QSize frame) const {
@ -518,7 +518,7 @@ void Sticker::paint(Painter &p, const QRect &clip, const PaintContext *context)
.frame = size, .frame = size,
.keepAlpha = true, .keepAlpha = true,
}, context->paused ? 0 : context->ms); }, context->paused ? 0 : context->ms);
p.drawPixmap( p.drawImage(
(st::stickerPanSize.width() - size.width()) / 2, (st::stickerPanSize.width() - size.width()) / 2,
(st::stickerPanSize.height() - size.width()) / 2, (st::stickerPanSize.height() - size.width()) / 2,
frame); frame);
@ -1519,7 +1519,7 @@ void Game::paint(Painter &p, const QRect &clip, const PaintContext *context) con
_thumb = pixmap; _thumb = pixmap;
_thumbGood = true; _thumbGood = true;
} }
p.drawPixmapLeft(rthumb.topLeft(), _width, pixmap); p.drawImage(rthumb.topLeft(), pixmap);
thumbDisplayed = true; thumbDisplayed = true;
} }
} }
@ -1529,7 +1529,7 @@ void Game::paint(Painter &p, const QRect &clip, const PaintContext *context) con
if (_thumb.isNull()) { if (_thumb.isNull()) {
p.fillRect(rthumb, st::overviewPhotoBg); p.fillRect(rthumb, st::overviewPhotoBg);
} else { } else {
p.drawPixmapLeft(rthumb.topLeft(), _width, _thumb); p.drawImage(rthumb.topLeft(), _thumb);
} }
} }
@ -1630,7 +1630,7 @@ void Game::validateThumbnail(Image *image, QSize size, bool good) const {
.options = (Images::Option::TransparentBackground .options = (Images::Option::TransparentBackground
| (good ? Images::Option() : Images::Option::Blur)), | (good ? Images::Option() : Images::Option::Blur)),
.outer = size, .outer = size,
}); }).toImage();
} }
bool Game::isRadialAnimation() const { bool Game::isRadialAnimation() const {

View file

@ -126,7 +126,7 @@ private:
Media::Clip::ReaderPointer _gif; Media::Clip::ReaderPointer _gif;
ClickHandlerPtr _delete; ClickHandlerPtr _delete;
mutable QPixmap _thumb; mutable QImage _thumb;
mutable bool _thumbGood = false; mutable bool _thumbGood = false;
mutable std::shared_ptr<Data::DocumentMedia> _dataMedia; mutable std::shared_ptr<Data::DocumentMedia> _dataMedia;
@ -419,7 +419,7 @@ private:
Media::Clip::ReaderPointer _gif; Media::Clip::ReaderPointer _gif;
mutable std::shared_ptr<Data::PhotoMedia> _photoMedia; mutable std::shared_ptr<Data::PhotoMedia> _photoMedia;
mutable std::shared_ptr<Data::DocumentMedia> _documentMedia; mutable std::shared_ptr<Data::DocumentMedia> _documentMedia;
mutable QPixmap _thumb; mutable QImage _thumb;
mutable bool _thumbGood = false; mutable bool _thumbGood = false;
mutable std::unique_ptr<Ui::RadialAnimation> _radial; mutable std::unique_ptr<Ui::RadialAnimation> _radial;
Ui::Text::String _title, _description; Ui::Text::String _title, _description;

View file

@ -91,6 +91,7 @@ ReaderImplementation::ReadResult FFMpegReaderImplementation::readNextFrame() {
_frameMs = 0; _frameMs = 0;
_lastReadVideoMs = _lastReadAudioMs = 0; _lastReadVideoMs = _lastReadAudioMs = 0;
_skippedInvalidDataPackets = 0; _skippedInvalidDataPackets = 0;
_frameIndex = -1;
continue; continue;
} else if (res != AVERROR(EAGAIN)) { } else if (res != AVERROR(EAGAIN)) {
@ -162,6 +163,7 @@ void FFMpegReaderImplementation::processReadFrame() {
_hadFrame = _frameRead = true; _hadFrame = _frameRead = true;
_frameTime += _currentFrameDelay; _frameTime += _currentFrameDelay;
++_frameIndex;
} }
ReaderImplementation::ReadResult FFMpegReaderImplementation::readFramesTill(crl::time frameMs, crl::time systemMs) { ReaderImplementation::ReadResult FFMpegReaderImplementation::readFramesTill(crl::time frameMs, crl::time systemMs) {
@ -200,10 +202,15 @@ crl::time FFMpegReaderImplementation::durationMs() const {
return 0; return 0;
} }
bool FFMpegReaderImplementation::renderFrame(QImage &to, bool &hasAlpha, const QSize &size) { bool FFMpegReaderImplementation::renderFrame(
QImage &to,
bool &hasAlpha,
int &index,
const QSize &size) {
Expects(_frameRead); Expects(_frameRead);
_frameRead = false;
_frameRead = false;
index = _frameIndex;
if (!_width || !_height) { if (!_width || !_height) {
_width = _frame->width; _width = _frame->width;
_height = _frame->height; _height = _frame->height;

View file

@ -33,7 +33,11 @@ public:
crl::time frameRealTime() const override; crl::time frameRealTime() const override;
crl::time framePresentationTime() const override; crl::time framePresentationTime() const override;
bool renderFrame(QImage &to, bool &hasAlpha, const QSize &size) override; bool renderFrame(
QImage &to,
bool &hasAlpha,
int &index,
const QSize &size) override;
crl::time durationMs() const override; crl::time durationMs() const override;
@ -85,6 +89,7 @@ private:
AVCodecContext *_codecContext = nullptr; AVCodecContext *_codecContext = nullptr;
int _streamId = 0; int _streamId = 0;
FFmpeg::FramePointer _frame; FFmpeg::FramePointer _frame;
int _frameIndex = -1;
bool _opened = false; bool _opened = false;
bool _hadFrame = false; bool _hadFrame = false;
bool _frameRead = false; bool _frameRead = false;

View file

@ -42,7 +42,11 @@ public:
virtual crl::time framePresentationTime() const = 0; virtual crl::time framePresentationTime() const = 0;
// Render current frame to an image with specific size. // Render current frame to an image with specific size.
virtual bool renderFrame(QImage &to, bool &hasAlpha, const QSize &size) = 0; virtual bool renderFrame(
QImage &to,
bool &hasAlpha,
int &index,
const QSize &size) = 0;
virtual crl::time durationMs() const = 0; virtual crl::time durationMs() const = 0;

View file

@ -35,11 +35,21 @@ constexpr auto kClipThreadsCount = 8;
constexpr auto kAverageGifSize = 320 * 240; constexpr auto kAverageGifSize = 320 * 240;
constexpr auto kWaitBeforeGifPause = crl::time(200); constexpr auto kWaitBeforeGifPause = crl::time(200);
QImage PrepareFrameImage(const FrameRequest &request, const QImage &original, bool hasAlpha, QImage &cache) { QImage PrepareFrame(
const FrameRequest &request,
const QImage &original,
bool hasAlpha,
QImage &cache) {
const auto needResize = (original.size() != request.frame); const auto needResize = (original.size() != request.frame);
const auto needOuterFill = request.outer.isValid() && (request.outer != request.frame); const auto needOuterFill = request.outer.isValid()
&& (request.outer != request.frame);
const auto needRounding = (request.radius != ImageRoundRadius::None); const auto needRounding = (request.radius != ImageRoundRadius::None);
if (!needResize && !needOuterFill && !hasAlpha && !needRounding) { const auto colorizing = (request.colored.alpha() != 0);
if (!needResize
&& !needOuterFill
&& !hasAlpha
&& !needRounding
&& !colorizing) {
return original; return original;
} }
@ -89,13 +99,12 @@ QImage PrepareFrameImage(const FrameRequest &request, const QImage &original, bo
request.radius, request.radius,
request.corners); request.corners);
} }
if (colorizing) {
cache = Images::Colored(std::move(cache), request.colored);
}
return cache; return cache;
} }
QPixmap PrepareFrame(const FrameRequest &request, const QImage &original, bool hasAlpha, QImage &cache) {
return QPixmap::fromImage(PrepareFrameImage(request, original, hasAlpha, cache), Qt::ColorOnly);
}
} // namespace } // namespace
enum class ProcessResult { enum class ProcessResult {
@ -254,15 +263,18 @@ Reader::Frame *Reader::frameToWriteNext(bool checkNotWriting, int32 *index) cons
return _frames + i; return _frames + i;
} }
void Reader::moveToNextShow() const { bool Reader::moveToNextShow() const {
int32 step = _step.loadAcquire(); const auto step = _step.loadAcquire();
if (step == kWaitingForDimensionsStep) { if (step == kWaitingForDimensionsStep) {
} else if (step == kWaitingForRequestStep) { } else if (step == kWaitingForRequestStep) {
_step.storeRelease(kWaitingForFirstFrameStep); _step.storeRelease(kWaitingForFirstFrameStep);
return true;
} else if (step == kWaitingForFirstFrameStep) { } else if (step == kWaitingForFirstFrameStep) {
} else if (!(step % 2)) { } else if (!(step % 2)) {
_step.storeRelease(step + 1); _step.storeRelease(step + 1);
return true;
} }
return false;
} }
void Reader::moveToNextWrite() const { void Reader::moveToNextWrite() const {
@ -311,7 +323,7 @@ void Reader::start(FrameRequest request) {
Workers[_threadIndex]->manager.start(this); Workers[_threadIndex]->manager.start(this);
} }
QPixmap Reader::current(FrameRequest request, crl::time now) { Reader::FrameInfo Reader::frameInfo(FrameRequest request, crl::time now) {
Expects(!(request.outer.isValid() Expects(!(request.outer.isValid()
? request.outer ? request.outer
: request.frame).isEmpty()); : request.frame).isEmpty());
@ -346,31 +358,32 @@ QPixmap Reader::current(FrameRequest request, crl::time now) {
Assert(frame->request.radius == request.radius Assert(frame->request.radius == request.radius
&& frame->request.corners == request.corners && frame->request.corners == request.corners
&& frame->request.keepAlpha == request.keepAlpha); && frame->request.keepAlpha == request.keepAlpha);
if (frame->pix.size() == size) { if (frame->prepared.size() != size
moveToNextShow(); || frame->preparedColored != request.colored) {
return frame->pix; frame->request.frame = request.frame;
frame->request.outer = request.outer;
frame->request.colored = request.colored;
QImage cacheForResize;
frame->original.setDevicePixelRatio(factor);
frame->prepared = QImage();
frame->prepared = PrepareFrame(
frame->request,
frame->original,
true,
cacheForResize);
frame->preparedColored = request.colored;
auto other = frameToWriteNext(true);
if (other) other->request = frame->request;
if (Workers.size() <= _threadIndex) {
error();
} else if (_state != State::Error) {
Workers[_threadIndex]->manager.update(this);
}
} }
return { frame->prepared, frame->index };
frame->request.frame = request.frame;
frame->request.outer = request.outer;
QImage cacheForResize;
frame->original.setDevicePixelRatio(factor);
frame->pix = QPixmap();
frame->pix = PrepareFrame(frame->request, frame->original, true, cacheForResize);
auto other = frameToWriteNext(true);
if (other) other->request = frame->request;
moveToNextShow();
if (Workers.size() <= _threadIndex) {
error();
} else if (_state != State::Error) {
Workers[_threadIndex]->manager.update(this);
}
return frame->pix;
} }
bool Reader::ready() const { bool Reader::ready() const {
@ -478,7 +491,7 @@ public:
if (reader->start(internal::ReaderImplementation::Mode::Silent, firstFramePositionMs)) { if (reader->start(internal::ReaderImplementation::Mode::Silent, firstFramePositionMs)) {
auto firstFrameReadResult = reader->readFramesTill(-1, ms); auto firstFrameReadResult = reader->readFramesTill(-1, ms);
if (firstFrameReadResult == internal::ReaderImplementation::ReadResult::Success) { if (firstFrameReadResult == internal::ReaderImplementation::ReadResult::Success) {
if (reader->renderFrame(frame()->original, frame()->alpha, QSize())) { if (reader->renderFrame(frame()->original, frame()->alpha, frame()->index, QSize())) {
frame()->original.fill(QColor(0, 0, 0)); frame()->original.fill(QColor(0, 0, 0));
frame()->positionMs = _seekPositionMs; frame()->positionMs = _seekPositionMs;
@ -495,7 +508,7 @@ public:
} else if (readResult != internal::ReaderImplementation::ReadResult::Success) { // Read the first frame. } else if (readResult != internal::ReaderImplementation::ReadResult::Success) { // Read the first frame.
return error(); return error();
} }
if (!_implementation->renderFrame(frame()->original, frame()->alpha, QSize())) { if (!_implementation->renderFrame(frame()->original, frame()->alpha, frame()->index, QSize())) {
return error(); return error();
} }
frame()->positionMs = _implementation->frameRealTime(); frame()->positionMs = _implementation->frameRealTime();
@ -555,12 +568,17 @@ public:
bool renderFrame() { bool renderFrame() {
Expects(_request.valid()); Expects(_request.valid());
if (!_implementation->renderFrame(frame()->original, frame()->alpha, _request.frame)) { if (!_implementation->renderFrame(frame()->original, frame()->alpha, frame()->index, _request.frame)) {
return false; return false;
} }
frame()->original.setDevicePixelRatio(_request.factor); frame()->original.setDevicePixelRatio(_request.factor);
frame()->pix = QPixmap(); frame()->prepared = QImage();
frame()->pix = PrepareFrame(_request, frame()->original, frame()->alpha, frame()->cache); frame()->prepared = PrepareFrame(
_request,
frame()->original,
frame()->alpha,
frame()->cache);
frame()->preparedColored = _request.colored;
frame()->when = _nextFrameWhen; frame()->when = _nextFrameWhen;
frame()->positionMs = _nextFramePositionMs; frame()->positionMs = _nextFramePositionMs;
return true; return true;
@ -638,8 +656,11 @@ private:
FrameRequest _request; FrameRequest _request;
struct Frame { struct Frame {
QPixmap pix; QImage prepared;
QImage original, cache; QColor preparedColored = QColor(0, 0, 0, 0);
QImage original;
QImage cache;
int index = 0;
bool alpha = true; bool alpha = true;
crl::time when = 0; crl::time when = 0;
@ -780,8 +801,10 @@ bool Manager::handleProcessResult(ReaderPrivate *reader, ProcessResult result, c
Assert(reader->_frame >= 0); Assert(reader->_frame >= 0);
auto frame = it.key()->_frames + reader->_frame; auto frame = it.key()->_frames + reader->_frame;
frame->clear(); frame->clear();
frame->pix = reader->frame()->pix; frame->prepared = reader->frame()->prepared;
frame->preparedColored = reader->frame()->preparedColored;
frame->original = reader->frame()->original; frame->original = reader->frame()->original;
frame->index = reader->frame()->index;
frame->displayed.storeRelease(0); frame->displayed.storeRelease(0);
frame->positionMs = reader->frame()->positionMs; frame->positionMs = reader->frame()->positionMs;
if (result == ProcessResult::Started) { if (result == ProcessResult::Started) {
@ -959,10 +982,11 @@ Ui::PreparedFileInformation::Video PrepareForSending(const QString &fname, const
// return result; // return result;
// } // }
//} //}
auto index = 0;
auto hasAlpha = false; auto hasAlpha = false;
auto readResult = reader->readFramesTill(-1, crl::now()); auto readResult = reader->readFramesTill(-1, crl::now());
auto readFrame = (readResult == internal::ReaderImplementation::ReadResult::Success); auto readFrame = (readResult == internal::ReaderImplementation::ReadResult::Success);
if (readFrame && reader->renderFrame(result.thumbnail, hasAlpha, QSize())) { if (readFrame && reader->renderFrame(result.thumbnail, hasAlpha, index, QSize())) {
if (hasAlpha && !result.isWebmSticker) { if (hasAlpha && !result.isWebmSticker) {
result.thumbnail = Images::Opaque(std::move(result.thumbnail)); result.thumbnail = Images::Opaque(std::move(result.thumbnail));
} }

View file

@ -27,14 +27,16 @@ enum class State {
}; };
struct FrameRequest { struct FrameRequest {
bool valid() const { [[nodiscard]] bool valid() const {
return factor > 0; return factor > 0;
} }
QSize frame; QSize frame;
QSize outer; QSize outer;
int factor = 0; int factor = 0;
ImageRoundRadius radius = ImageRoundRadius::None; ImageRoundRadius radius = ImageRoundRadius::None;
RectParts corners = RectPart::AllCorners; RectParts corners = RectPart::AllCorners;
QColor colored = QColor(0, 0, 0, 0);
bool keepAlpha = false; bool keepAlpha = false;
}; };
@ -74,14 +76,27 @@ public:
Notification notification); Notification notification);
void start(FrameRequest request); void start(FrameRequest request);
[[nodiscard]] QPixmap current(FrameRequest request, crl::time now);
[[nodiscard]] QPixmap frameOriginal() const { struct FrameInfo {
QImage image;
int index = 0;
};
[[nodiscard]] FrameInfo frameInfo(FrameRequest request, crl::time now);
[[nodiscard]] QImage current(FrameRequest request, crl::time now) {
auto result = frameInfo(request, now).image;
moveToNextFrame();
return result;
}
[[nodiscard]] QImage frameOriginal() const {
if (const auto frame = frameToShow()) { if (const auto frame = frameToShow()) {
auto result = QPixmap::fromImage(frame->original); auto result = frame->original;
result.detach(); result.detach();
return result; return result;
} }
return QPixmap(); return QImage();
}
bool moveToNextFrame() {
return moveToNextShow();
} }
[[nodiscard]] bool currentDisplayed() const { [[nodiscard]] bool currentDisplayed() const {
const auto frame = frameToShow(); const auto frame = frameToShow();
@ -130,13 +145,17 @@ private:
mutable QAtomicInt _step = kWaitingForDimensionsStep; mutable QAtomicInt _step = kWaitingForDimensionsStep;
struct Frame { struct Frame {
void clear() { void clear() {
pix = QPixmap(); prepared = QImage();
preparedColored = QColor(0, 0, 0, 0);
original = QImage(); original = QImage();
} }
QPixmap pix;
QImage prepared;
QColor preparedColored = QColor(0, 0, 0, 0);
QImage original; QImage original;
FrameRequest request; FrameRequest request;
QAtomicInt displayed = 0; QAtomicInt displayed = 0;
int index = 0;
// Should be counted from the end, // Should be counted from the end,
// so that positionMs <= _durationMs. // so that positionMs <= _durationMs.
@ -146,7 +165,7 @@ private:
Frame *frameToShow(int *index = nullptr) const; // 0 means not ready Frame *frameToShow(int *index = nullptr) const; // 0 means not ready
Frame *frameToWrite(int *index = nullptr) const; // 0 means not ready Frame *frameToWrite(int *index = nullptr) const; // 0 means not ready
Frame *frameToWriteNext(bool check, int *index = nullptr) const; Frame *frameToWriteNext(bool check, int *index = nullptr) const;
void moveToNextShow() const; bool moveToNextShow() const;
void moveToNextWrite() const; void moveToNextWrite() const;
QAtomicInt _autoPausedGif = 0; QAtomicInt _autoPausedGif = 0;

View file

@ -1974,7 +1974,7 @@ void Gif::validateThumbnail(
{ {
.options = (good ? Images::Option() : Images::Option::Blur), .options = (good ? Images::Option() : Images::Option::Blur),
.outer = size, .outer = size,
}); }).toImage();
} }
void Gif::prepareThumbnail(QSize size, QSize frame) { void Gif::prepareThumbnail(QSize size, QSize frame) {
@ -2031,13 +2031,13 @@ void Gif::paint(
_thumb = pixmap; _thumb = pixmap;
_thumbGood = true; _thumbGood = true;
} }
p.drawPixmap(r.topLeft(), pixmap); p.drawImage(r.topLeft(), pixmap);
} else { } else {
prepareThumbnail(r.size(), frame); prepareThumbnail(r.size(), frame);
if (_thumb.isNull()) { if (_thumb.isNull()) {
p.fillRect(r, st::overviewPhotoBg); p.fillRect(r, st::overviewPhotoBg);
} else { } else {
p.drawPixmap(r.topLeft(), _thumb); p.drawImage(r.topLeft(), _thumb);
} }
} }

View file

@ -255,7 +255,7 @@ private:
mutable std::shared_ptr<Data::DocumentMedia> _dataMedia; mutable std::shared_ptr<Data::DocumentMedia> _dataMedia;
StatusText _status; StatusText _status;
QPixmap _thumb; QImage _thumb;
bool _thumbGood = false; bool _thumbGood = false;
}; };

View file

@ -80,7 +80,7 @@ bool SingleMediaPreview::tryPaintAnimation(Painter &p) {
const auto frame = _gifPreview->current({ const auto frame = _gifPreview->current({
.frame = QSize(previewWidth(), previewHeight()), .frame = QSize(previewWidth(), previewHeight()),
}, paused ? 0 : crl::now()); }, paused ? 0 : crl::now());
p.drawPixmap(previewLeft(), previewTop(), frame); p.drawImage(previewLeft(), previewTop(), frame);
return true; return true;
} else if (_lottiePreview && _lottiePreview->ready()) { } else if (_lottiePreview && _lottiePreview->ready()) {
const auto frame = _lottiePreview->frame(); const auto frame = _lottiePreview->frame();

View file

@ -381,9 +381,9 @@ QPixmap MediaPreviewWidget::currentImage() const {
if (gif && gif->started()) { if (gif && gif->started()) {
const auto paused = _controller->isGifPausedAtLeastFor( const auto paused = _controller->isGifPausedAtLeastFor(
Window::GifPauseReason::MediaPreview); Window::GifPauseReason::MediaPreview);
return gif->current( return QPixmap::fromImage(gif->current(
{ .frame = currentDimensions(), .keepAlpha = webm }, { .frame = currentDimensions(), .keepAlpha = webm },
paused ? 0 : crl::now()); paused ? 0 : crl::now()), Qt::ColorOnly);
} }
if (_cacheStatus != CacheThumbLoaded if (_cacheStatus != CacheThumbLoaded
&& _document->hasThumbnail()) { && _document->hasThumbnail()) {