mirror of
https://github.com/AyuGram/AyuGramDesktop.git
synced 2025-04-17 22:57:11 +02:00
Move Webm sticker to UnwrappedMedia.
This commit is contained in:
parent
5b0d023a88
commit
f8e22210e7
33 changed files with 458 additions and 300 deletions
|
@ -1283,7 +1283,7 @@ void StickerSetBox::Inner::paintSticker(
|
|||
|
||||
_lottiePlayer->unpause(element.lottie);
|
||||
} else if (element.webm && element.webm->started()) {
|
||||
p.drawPixmap(ppos, element.webm->current({
|
||||
p.drawImage(ppos, element.webm->current({
|
||||
.frame = size,
|
||||
.keepAlpha = true,
|
||||
}, paused ? 0 : now));
|
||||
|
|
|
@ -1421,10 +1421,9 @@ void StickersBox::Inner::paintRowThumbnail(
|
|||
row->lottie->markFrameShown();
|
||||
}
|
||||
} else if (row->webm && row->webm->started()) {
|
||||
p.drawPixmapLeft(
|
||||
p.drawImage(
|
||||
x,
|
||||
y,
|
||||
width(),
|
||||
row->webm->current(
|
||||
{ .frame = { row->pixw, row->pixh }, .keepAlpha = true },
|
||||
paused ? 0 : crl::now()));
|
||||
|
|
|
@ -882,7 +882,7 @@ void FieldAutocomplete::Inner::paintEvent(QPaintEvent *e) {
|
|||
sticker.lottie->markFrameShown();
|
||||
}
|
||||
} else if (sticker.webm && sticker.webm->started()) {
|
||||
p.drawPixmap(ppos, sticker.webm->current({
|
||||
p.drawImage(ppos, sticker.webm->current({
|
||||
.frame = size,
|
||||
.keepAlpha = true,
|
||||
}, paused ? 0 : now));
|
||||
|
|
|
@ -208,7 +208,7 @@ void StickersListFooter::clearHeavyData() {
|
|||
icon.lifetime.destroy();
|
||||
icon.stickerMedia = nullptr;
|
||||
if (!info.visible) {
|
||||
icon.savedFrame = QPixmap();
|
||||
icon.savedFrame = QImage();
|
||||
}
|
||||
return true;
|
||||
});
|
||||
|
@ -1142,7 +1142,7 @@ void StickersListFooter::paintSetIcon(
|
|||
const auto frame = icon.lottie->frame();
|
||||
const auto size = frame.size() / cIntRetinaFactor();
|
||||
if (icon.savedFrame.isNull()) {
|
||||
icon.savedFrame = QPixmap::fromImage(frame, Qt::ColorOnly);
|
||||
icon.savedFrame = frame;
|
||||
icon.savedFrame.setDevicePixelRatio(cRetinaFactor());
|
||||
}
|
||||
p.drawImage(
|
||||
|
@ -1163,17 +1163,17 @@ void StickersListFooter::paintSetIcon(
|
|||
icon.savedFrame = frame;
|
||||
icon.savedFrame.setDevicePixelRatio(cRetinaFactor());
|
||||
}
|
||||
p.drawPixmapLeft(x, y, width(), frame);
|
||||
} else if (!icon.savedFrame.isNull() || thumb) {
|
||||
const auto pixmap = !icon.savedFrame.isNull()
|
||||
? icon.savedFrame
|
||||
: (!icon.lottie && thumb)
|
||||
p.drawImage(x, y, frame);
|
||||
} else if (!icon.savedFrame.isNull()) {
|
||||
p.drawImage(x, y, icon.savedFrame);
|
||||
} else if (thumb) {
|
||||
const auto pixmap = (!icon.lottie && thumb)
|
||||
? thumb->pix(icon.pixw, icon.pixh)
|
||||
: QPixmap();
|
||||
if (pixmap.isNull()) {
|
||||
return;
|
||||
} else if (icon.savedFrame.isNull()) {
|
||||
icon.savedFrame = pixmap;
|
||||
icon.savedFrame = pixmap.toImage();
|
||||
}
|
||||
p.drawPixmapLeft(x, y, width(), pixmap);
|
||||
}
|
||||
|
|
|
@ -63,7 +63,7 @@ struct StickerIcon {
|
|||
Data::StickersSet *set = nullptr;
|
||||
mutable std::unique_ptr<Lottie::SinglePlayer> lottie;
|
||||
mutable Media::Clip::ReaderPointer webm;
|
||||
mutable QPixmap savedFrame;
|
||||
mutable QImage savedFrame;
|
||||
DocumentData *sticker = nullptr;
|
||||
ChannelData *megagroup = nullptr;
|
||||
mutable std::shared_ptr<Data::StickersSetThumbnailView> thumbnailMedia;
|
||||
|
|
|
@ -76,7 +76,7 @@ struct StickersListWidget::Sticker {
|
|||
std::shared_ptr<Data::DocumentMedia> documentMedia;
|
||||
Lottie::Animation *lottie = nullptr;
|
||||
Media::Clip::ReaderPointer webm;
|
||||
QPixmap savedFrame;
|
||||
QImage savedFrame;
|
||||
QSize savedFrameFor;
|
||||
QImage premiumLock;
|
||||
|
||||
|
@ -983,7 +983,7 @@ void StickersListWidget::clearHeavyIn(Set &set, bool clearSavedFrames) {
|
|||
const auto lifetime = base::take(set.lottieLifetime);
|
||||
for (auto &sticker : set.stickers) {
|
||||
if (clearSavedFrames) {
|
||||
sticker.savedFrame = QPixmap();
|
||||
sticker.savedFrame = QImage();
|
||||
sticker.savedFrameFor = QSize();
|
||||
}
|
||||
sticker.webm = nullptr;
|
||||
|
@ -1314,9 +1314,7 @@ void StickersListWidget::paintSticker(
|
|||
QRect(ppos, lottieFrame.size() / cIntRetinaFactor()),
|
||||
lottieFrame);
|
||||
if (sticker.savedFrame.isNull()) {
|
||||
sticker.savedFrame = QPixmap::fromImage(
|
||||
lottieFrame,
|
||||
Qt::ColorOnly);
|
||||
sticker.savedFrame = lottieFrame;
|
||||
sticker.savedFrame.setDevicePixelRatio(cRetinaFactor());
|
||||
sticker.savedFrameFor = _singleSize;
|
||||
}
|
||||
|
@ -1330,20 +1328,22 @@ void StickersListWidget::paintSticker(
|
|||
sticker.savedFrame.setDevicePixelRatio(cRetinaFactor());
|
||||
sticker.savedFrameFor = _singleSize;
|
||||
}
|
||||
p.drawPixmapLeft(ppos, width(), frame);
|
||||
p.drawImage(ppos, frame);
|
||||
} else {
|
||||
const auto image = media->getStickerSmall();
|
||||
const auto useSavedFrame = !sticker.savedFrame.isNull()
|
||||
&& (sticker.savedFrameFor == _singleSize);
|
||||
const auto pixmap = useSavedFrame
|
||||
? sticker.savedFrame
|
||||
: image
|
||||
? image->pixSingle(size, { .outer = size })
|
||||
: QPixmap();
|
||||
if (!pixmap.isNull()) {
|
||||
if (useSavedFrame) {
|
||||
p.drawImage(ppos, sticker.savedFrame);
|
||||
if (premium) {
|
||||
lottieFrame = sticker.savedFrame;
|
||||
}
|
||||
} else if (image) {
|
||||
const auto pixmap = image->pixSingle(size, { .outer = size });
|
||||
p.drawPixmapLeft(ppos, width(), pixmap);
|
||||
if (sticker.savedFrame.isNull()) {
|
||||
sticker.savedFrame = pixmap;
|
||||
sticker.savedFrame = pixmap.toImage().convertToFormat(
|
||||
QImage::Format_ARGB32_Premultiplied);
|
||||
sticker.savedFrameFor = _singleSize;
|
||||
}
|
||||
if (premium) {
|
||||
|
|
|
@ -990,7 +990,7 @@ std::unique_ptr<HistoryView::Media> MediaFile::createView(
|
|||
not_null<HistoryView::Element*> message,
|
||||
not_null<HistoryItem*> realParent,
|
||||
HistoryView::Element *replacing) {
|
||||
if (const auto info = _document->sticker(); info && !info->isWebm()) {
|
||||
if (_document->sticker()) {
|
||||
return std::make_unique<HistoryView::UnwrappedMedia>(
|
||||
message,
|
||||
std::make_unique<HistoryView::Sticker>(
|
||||
|
@ -998,9 +998,7 @@ std::unique_ptr<HistoryView::Media> MediaFile::createView(
|
|||
_document,
|
||||
_skipPremiumEffect,
|
||||
replacing));
|
||||
} else if (_document->isAnimation()
|
||||
|| _document->isVideoFile()
|
||||
|| (info && info->isWebm())) {
|
||||
} else if (_document->isAnimation() || _document->isVideoFile()) {
|
||||
return std::make_unique<HistoryView::Gif>(
|
||||
message,
|
||||
realParent,
|
||||
|
|
|
@ -86,7 +86,7 @@ void VideoUserpic::paintLeft(
|
|||
startReady();
|
||||
|
||||
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 {
|
||||
_peer->paintUserpicLeft(p, view, x, y, w, size);
|
||||
}
|
||||
|
|
|
@ -34,7 +34,7 @@ ItemSticker::ItemSticker(
|
|||
}
|
||||
const auto updateThumbnail = [=] {
|
||||
const auto guard = gsl::finally([&] {
|
||||
if (_pixmap.isNull()) {
|
||||
if (_image.isNull()) {
|
||||
setAspectRatio(1.);
|
||||
}
|
||||
});
|
||||
|
@ -47,8 +47,7 @@ ItemSticker::ItemSticker(
|
|||
Lottie::Quality::High);
|
||||
_lottie.player->updates(
|
||||
) | rpl::start_with_next([=] {
|
||||
updatePixmap(Ui::PixmapFromImage(
|
||||
_lottie.player->frame()));
|
||||
updatePixmap(_lottie.player->frame());
|
||||
_lottie.player = nullptr;
|
||||
_lottie.lifetime.destroy();
|
||||
update();
|
||||
|
@ -81,7 +80,7 @@ ItemSticker::ItemSticker(
|
|||
const auto ratio = style::DevicePixelRatio();
|
||||
auto pixmap = sticker->pixNoCache(sticker->size() * ratio);
|
||||
pixmap.setDevicePixelRatio(ratio);
|
||||
updatePixmap(std::move(pixmap));
|
||||
updatePixmap(pixmap.toImage());
|
||||
return true;
|
||||
};
|
||||
if (!updateThumbnail()) {
|
||||
|
@ -95,15 +94,15 @@ ItemSticker::ItemSticker(
|
|||
}
|
||||
}
|
||||
|
||||
void ItemSticker::updatePixmap(QPixmap &&pixmap) {
|
||||
_pixmap = std::move(pixmap);
|
||||
void ItemSticker::updatePixmap(QImage &&image) {
|
||||
_image = std::move(image);
|
||||
if (flipped()) {
|
||||
performFlip();
|
||||
} else {
|
||||
update();
|
||||
}
|
||||
if (!_pixmap.isNull()) {
|
||||
setAspectRatio(_pixmap.height() / float64(_pixmap.width()));
|
||||
if (!_image.isNull()) {
|
||||
setAspectRatio(_image.height() / float64(_image.width()));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -111,7 +110,7 @@ void ItemSticker::paint(
|
|||
QPainter *p,
|
||||
const QStyleOptionGraphicsItem *option,
|
||||
QWidget *w) {
|
||||
p->drawPixmap(contentRect().toRect(), _pixmap);
|
||||
p->drawImage(contentRect().toRect(), _image);
|
||||
ItemBase::paint(p, option, w);
|
||||
}
|
||||
|
||||
|
@ -124,7 +123,7 @@ int ItemSticker::type() const {
|
|||
}
|
||||
|
||||
void ItemSticker::performFlip() {
|
||||
_pixmap = _pixmap.transformed(QTransform().scale(-1, 1));
|
||||
_image = _image.transformed(QTransform().scale(-1, 1));
|
||||
update();
|
||||
}
|
||||
|
||||
|
|
|
@ -42,14 +42,14 @@ private:
|
|||
const not_null<DocumentData*> _document;
|
||||
const std::shared_ptr<::Data::DocumentMedia> _mediaView;
|
||||
|
||||
void updatePixmap(QPixmap &&pixmap);
|
||||
void updatePixmap(QImage &&image);
|
||||
|
||||
struct {
|
||||
std::unique_ptr<Lottie::SinglePlayer> player;
|
||||
rpl::lifetime lifetime;
|
||||
} _lottie;
|
||||
::Media::Clip::ReaderPointer _webm;
|
||||
QPixmap _pixmap;
|
||||
QImage _image;
|
||||
|
||||
rpl::lifetime _loadingLifetime;
|
||||
|
||||
|
|
|
@ -78,7 +78,9 @@ void Dice::draw(Painter &p, const PaintContext &context, const QRect &r) {
|
|||
_end->draw(p, context, r);
|
||||
} else if (_start) {
|
||||
_start->draw(p, context, r);
|
||||
if (_end && _end->readyToDrawLottie() && _start->atTheEnd()) {
|
||||
if (_end
|
||||
&& _end->readyToDrawAnimationFrame()
|
||||
&& _start->atTheEnd()) {
|
||||
_drawingEnd = true;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -24,7 +24,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
#include "history/view/history_view_element.h"
|
||||
#include "history/view/history_view_cursor_state.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 "core/application.h" // Application::showDocument.
|
||||
#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_origin.h"
|
||||
#include "data/data_document_media.h"
|
||||
#include "chat_helpers/stickers_lottie.h" // PaintStickerThumbnailPath.
|
||||
#include "styles/style_chat.h"
|
||||
|
||||
namespace HistoryView {
|
||||
|
@ -81,12 +79,7 @@ Gif::Gif(
|
|||
, _data(document)
|
||||
, _caption(st::minPhotoSize - st::msgPadding.left() - st::msgPadding.right())
|
||||
, _downloadSize(Ui::FormatSizeText(_data->size)) {
|
||||
if (const auto info = _data->sticker(); info && info->set) {
|
||||
_stickerLink = Sticker::ShowSetHandler(_data);
|
||||
} else {
|
||||
setDocumentLinks(_data, realParent);
|
||||
}
|
||||
|
||||
setDocumentLinks(_data, realParent);
|
||||
setStatusSize(Ui::FileStatusSizeReady);
|
||||
|
||||
refreshCaption();
|
||||
|
@ -132,17 +125,13 @@ QSize Gif::sizeForAspectRatio() const {
|
|||
}
|
||||
|
||||
QSize Gif::countThumbSize(int &inOutWidthMax) const {
|
||||
const auto maxSize = _data->sticker()
|
||||
? Sticker::Size().width()
|
||||
: _data->isVideoFile()
|
||||
const auto maxSize = _data->isVideoFile()
|
||||
? st::maxMediaSize
|
||||
: _data->isVideoMessage()
|
||||
? st::maxVideoMessageSize
|
||||
: st::maxGifSize;
|
||||
const auto useMaxSize = std::max(maxSize, st::minPhotoSize);
|
||||
const auto size = _data->sticker()
|
||||
? videoSize()
|
||||
: style::ConvertScale(videoSize());
|
||||
const auto size = style::ConvertScale(videoSize());
|
||||
accumulate_min(inOutWidthMax, useMaxSize);
|
||||
return DownscaledSize(size, { inOutWidthMax, useMaxSize });
|
||||
}
|
||||
|
@ -275,9 +264,7 @@ void Gif::draw(Painter &p, const PaintContext &context) const {
|
|||
ensureDataMediaCreated();
|
||||
const auto item = _parent->data();
|
||||
const auto loaded = dataLoaded();
|
||||
const auto sticker = _data->sticker();
|
||||
const auto displayLoading = !sticker
|
||||
&& (item->isSending() || _data->displayLoading());
|
||||
const auto displayLoading = (item->isSending() || _data->displayLoading());
|
||||
const auto st = context.st;
|
||||
const auto sti = context.imageStyle();
|
||||
const auto stm = context.messageStyle();
|
||||
|
@ -330,9 +317,7 @@ void Gif::draw(Painter &p, const PaintContext &context) const {
|
|||
}
|
||||
updateStatusText();
|
||||
const auto radial = isRadialAnimation()
|
||||
|| (!sticker
|
||||
&& streamedForWaiting
|
||||
&& streamedForWaiting->waitingShown());
|
||||
|| (streamedForWaiting && streamedForWaiting->waitingShown());
|
||||
|
||||
if (bubble) {
|
||||
if (!_caption.isEmpty()) {
|
||||
|
@ -398,10 +383,6 @@ void Gif::draw(Painter &p, const PaintContext &context) const {
|
|||
auto request = ::Media::Streaming::FrameRequest();
|
||||
request.outer = QSize(usew, painth) * 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.radius = roundRadius;
|
||||
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 playOnce = sticker
|
||||
&& !Core::App().settings().loopAnimatedStickers();
|
||||
const auto switchToNext = !playOnce
|
||||
|| (frame.index != 0)
|
||||
|| !_stickerOncePlayed;
|
||||
p.drawImage(rthumb, frame.image);
|
||||
if (!paused
|
||||
&& switchToNext
|
||||
&& streamed->markFrameShown()
|
||||
&& playOnce
|
||||
&& !_stickerOncePlayed) {
|
||||
_stickerOncePlayed = true;
|
||||
_parent->delegate()->elementStartStickerLoop(_parent);
|
||||
if (!paused) {
|
||||
streamed->markFrameShown();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -464,13 +435,8 @@ void Gif::draw(Painter &p, const PaintContext &context) const {
|
|||
ensureDataMediaCreated();
|
||||
const auto size = QSize(_thumbw, _thumbh);
|
||||
const auto args = Images::PrepareArgs{
|
||||
.colored = ((sticker && context.selected())
|
||||
? &context.st->msgStickerOverlay()
|
||||
: nullptr),
|
||||
.options = (sticker
|
||||
? Images::Option::TransparentBackground
|
||||
: Images::RoundOptions(roundRadius, roundCorners)),
|
||||
.outer = sticker ? QSize() : QSize(usew, painth),
|
||||
.options = Images::RoundOptions(roundRadius, roundCorners),
|
||||
.outer = QSize(usew, painth),
|
||||
};
|
||||
if (const auto good = _dataMedia->goodThumbnail()) {
|
||||
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);
|
||||
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);
|
||||
}
|
||||
|
||||
if (radial
|
||||
|| (!sticker
|
||||
&& !streamingMode
|
||||
|| (!streamingMode
|
||||
&& ((!loaded && !_data->loading()) || !autoplay))) {
|
||||
const auto radialOpacity = (item->isSending() || _data->uploading())
|
||||
? 1.
|
||||
|
@ -725,28 +688,6 @@ void Gif::validateVideoThumbnail() const {
|
|||
: 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(
|
||||
Painter &p,
|
||||
const PaintContext &context,
|
||||
|
@ -923,9 +864,7 @@ TextState Gif::textState(QPoint point, StateRequest request) const {
|
|||
}
|
||||
if (QRect(usex + paintx, painty, usew, painth).contains(point)) {
|
||||
ensureDataMediaCreated();
|
||||
result.link = _data->sticker()
|
||||
? _stickerLink
|
||||
: _data->uploading()
|
||||
result.link = _data->uploading()
|
||||
? _cancell
|
||||
: _realParent->isSending()
|
||||
? nullptr
|
||||
|
@ -1241,7 +1180,7 @@ bool Gif::uploading() const {
|
|||
}
|
||||
|
||||
bool Gif::needsBubble() const {
|
||||
if (_data->sticker() || _data->isVideoMessage()) {
|
||||
if (_data->isVideoMessage()) {
|
||||
return false;
|
||||
} else if (!_caption.isEmpty()) {
|
||||
return true;
|
||||
|
@ -1333,8 +1272,7 @@ int Gif::additionalWidth() const {
|
|||
}
|
||||
|
||||
bool Gif::isUnwrapped() const {
|
||||
return (_data->sticker() || _data->isVideoMessage())
|
||||
&& (_parent->media() == this);
|
||||
return _data->isVideoMessage() && (_parent->media() == this);
|
||||
}
|
||||
|
||||
void Gif::validateGroupedCache(
|
||||
|
@ -1684,7 +1622,6 @@ bool Gif::needInfoDisplay() const {
|
|||
return _parent->data()->isSending()
|
||||
|| _data->uploading()
|
||||
|| _parent->isUnderCursor()
|
||||
|| (_data->sticker() && _parent->rightActionSize())
|
||||
// Don't show the GIF badge if this message has text.
|
||||
|| (!_parent->hasBubble() && _parent->isLastAndSelfMessage());
|
||||
}
|
||||
|
|
|
@ -101,10 +101,6 @@ public:
|
|||
QPoint resolveCustomInfoRightBottom() const override;
|
||||
QString additionalInfoString() const override;
|
||||
|
||||
void stickerClearLoopPlayed() override {
|
||||
_stickerOncePlayed = false;
|
||||
}
|
||||
|
||||
bool skipBubbleTail() const override {
|
||||
return isRoundedInBubbleBottom() && _caption.isEmpty();
|
||||
}
|
||||
|
@ -181,11 +177,6 @@ private:
|
|||
StateRequest request,
|
||||
QPoint position) const;
|
||||
|
||||
void paintPath(
|
||||
Painter &p,
|
||||
const PaintContext &context,
|
||||
const QRect &r) const;
|
||||
|
||||
const not_null<DocumentData*> _data;
|
||||
int _thumbw = 1;
|
||||
int _thumbh = 1;
|
||||
|
@ -193,10 +184,7 @@ private:
|
|||
std::unique_ptr<Streamed> _streamed;
|
||||
mutable std::shared_ptr<Data::DocumentMedia> _dataMedia;
|
||||
mutable std::unique_ptr<Image> _videoThumbnailFrame;
|
||||
ClickHandlerPtr _stickerLink;
|
||||
|
||||
QString _downloadSize;
|
||||
mutable bool _stickerOncePlayed = false;
|
||||
|
||||
};
|
||||
|
||||
|
|
|
@ -12,7 +12,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
#include "history/view/history_view_element.h"
|
||||
#include "history/view/history_view_cursor_state.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 "data/data_document.h"
|
||||
#include "data/data_session.h"
|
||||
|
@ -243,7 +243,7 @@ PointState Media::pointState(QPoint point) const {
|
|||
: PointState::Outside;
|
||||
}
|
||||
|
||||
std::unique_ptr<Lottie::SinglePlayer> Media::stickerTakeLottie(
|
||||
std::unique_ptr<StickerPlayer> Media::stickerTakePlayer(
|
||||
not_null<DocumentData*> data,
|
||||
const Lottie::ColorReplacements *replacements) {
|
||||
return nullptr;
|
||||
|
|
|
@ -25,7 +25,6 @@ using SharedMediaTypesMask = base::enum_mask<SharedMediaType>;
|
|||
} // namespace Storage
|
||||
|
||||
namespace Lottie {
|
||||
class SinglePlayer;
|
||||
struct ColorReplacements;
|
||||
} // namespace Lottie
|
||||
|
||||
|
@ -41,6 +40,7 @@ enum class CursorState : char;
|
|||
enum class InfoDisplayType : char;
|
||||
struct TextState;
|
||||
struct StateRequest;
|
||||
class StickerPlayer;
|
||||
class Element;
|
||||
|
||||
using PaintContext = Ui::ChatPaintContext;
|
||||
|
@ -172,7 +172,7 @@ public:
|
|||
}
|
||||
virtual void stickerClearLoopPlayed() {
|
||||
}
|
||||
virtual std::unique_ptr<Lottie::SinglePlayer> stickerTakeLottie(
|
||||
virtual std::unique_ptr<StickerPlayer> stickerTakePlayer(
|
||||
not_null<DocumentData*> data,
|
||||
const Lottie::ColorReplacements *replacements);
|
||||
virtual void checkAnimation() {
|
||||
|
|
|
@ -25,10 +25,9 @@ constexpr auto kMaxForwardedBarLines = 4;
|
|||
|
||||
} // namespace
|
||||
|
||||
auto UnwrappedMedia::Content::stickerTakeLottie(
|
||||
not_null<DocumentData*> data,
|
||||
const Lottie::ColorReplacements *replacements)
|
||||
-> std::unique_ptr<Lottie::SinglePlayer> {
|
||||
std::unique_ptr<StickerPlayer> UnwrappedMedia::Content::stickerTakePlayer(
|
||||
not_null<DocumentData*> data,
|
||||
const Lottie::ColorReplacements *replacements) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
@ -465,10 +464,10 @@ QPoint UnwrappedMedia::resolveCustomInfoRightBottom() const {
|
|||
return QPoint(fullRight - skipx, fullBottom - skipy);
|
||||
}
|
||||
|
||||
std::unique_ptr<Lottie::SinglePlayer> UnwrappedMedia::stickerTakeLottie(
|
||||
std::unique_ptr<StickerPlayer> UnwrappedMedia::stickerTakePlayer(
|
||||
not_null<DocumentData*> data,
|
||||
const Lottie::ColorReplacements *replacements) {
|
||||
return _content->stickerTakeLottie(data, replacements);
|
||||
return _content->stickerTakePlayer(data, replacements);
|
||||
}
|
||||
|
||||
//void UnwrappedMedia::externalLottieProgressing(bool external) {
|
||||
|
|
|
@ -38,7 +38,7 @@ public:
|
|||
}
|
||||
virtual void stickerClearLoopPlayed() {
|
||||
}
|
||||
virtual std::unique_ptr<Lottie::SinglePlayer> stickerTakeLottie(
|
||||
virtual std::unique_ptr<StickerPlayer> stickerTakePlayer(
|
||||
not_null<DocumentData*> data,
|
||||
const Lottie::ColorReplacements *replacements);
|
||||
|
||||
|
@ -104,7 +104,7 @@ public:
|
|||
void stickerClearLoopPlayed() override {
|
||||
_content->stickerClearLoopPlayed();
|
||||
}
|
||||
std::unique_ptr<Lottie::SinglePlayer> stickerTakeLottie(
|
||||
std::unique_ptr<StickerPlayer> stickerTakePlayer(
|
||||
not_null<DocumentData*> data,
|
||||
const Lottie::ColorReplacements *replacements) override;
|
||||
|
||||
|
|
|
@ -195,11 +195,11 @@ void MediaGift::stickerClearLoopPlayed() {
|
|||
}
|
||||
}
|
||||
|
||||
std::unique_ptr<Lottie::SinglePlayer> MediaGift::stickerTakeLottie(
|
||||
std::unique_ptr<StickerPlayer> MediaGift::stickerTakePlayer(
|
||||
not_null<DocumentData*> data,
|
||||
const Lottie::ColorReplacements *replacements) {
|
||||
return _sticker
|
||||
? _sticker->stickerTakeLottie(data, replacements)
|
||||
? _sticker->stickerTakePlayer(data, replacements)
|
||||
: nullptr;
|
||||
}
|
||||
|
||||
|
|
|
@ -42,7 +42,7 @@ public:
|
|||
bool pressed) override;
|
||||
|
||||
void stickerClearLoopPlayed() override;
|
||||
std::unique_ptr<Lottie::SinglePlayer> stickerTakeLottie(
|
||||
std::unique_ptr<StickerPlayer> stickerTakePlayer(
|
||||
not_null<DocumentData*> data,
|
||||
const Lottie::ColorReplacements *replacements) override;
|
||||
|
||||
|
|
|
@ -161,16 +161,16 @@ void SlotMachine::draw(
|
|||
// }
|
||||
//}
|
||||
auto switchedToEnd = _drawingEnd;
|
||||
const auto pullReady = _pull && _pull->readyToDrawLottie();
|
||||
const auto pullReady = _pull && _pull->readyToDrawAnimationFrame();
|
||||
const auto paintReady = [&] {
|
||||
auto result = pullReady;
|
||||
auto allPlayedEnough = true;
|
||||
for (auto i = 1; i != 4; ++i) {
|
||||
if (!_end[i] || !_end[i]->readyToDrawLottie()) {
|
||||
if (!_end[i] || !_end[i]->readyToDrawAnimationFrame()) {
|
||||
switchedToEnd[i] = false;
|
||||
}
|
||||
if (!switchedToEnd[i]
|
||||
&& (!_start[i] || !_start[i]->readyToDrawLottie())) {
|
||||
&& (!_start[i] || !_start[i]->readyToDrawAnimationFrame())) {
|
||||
result = false;
|
||||
}
|
||||
const auto playedTillFrame = !switchedToEnd[i]
|
||||
|
@ -180,11 +180,13 @@ void SlotMachine::draw(
|
|||
allPlayedEnough = false;
|
||||
}
|
||||
}
|
||||
if (!_end[0] || !_end[0]->readyToDrawLottie() || !allPlayedEnough) {
|
||||
if (!_end[0]
|
||||
|| !_end[0]->readyToDrawAnimationFrame()
|
||||
|| !allPlayedEnough) {
|
||||
switchedToEnd[0] = false;
|
||||
}
|
||||
if (ranges::contains(switchedToEnd, false)
|
||||
&& (!_start[0] || !_start[0]->readyToDrawLottie())) {
|
||||
&& (!_start[0] || !_start[0]->readyToDrawAnimationFrame())) {
|
||||
result = false;
|
||||
}
|
||||
return result;
|
||||
|
@ -200,7 +202,7 @@ void SlotMachine::draw(
|
|||
} else {
|
||||
_start[i]->draw(p, context, r);
|
||||
if (_end[i]
|
||||
&& _end[i]->readyToDrawLottie()
|
||||
&& _end[i]->readyToDrawAnimationFrame()
|
||||
&& _start[i]->atTheEnd()) {
|
||||
_drawingEnd[i] = true;
|
||||
}
|
||||
|
|
|
@ -31,6 +31,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
#include "data/data_file_click_handler.h"
|
||||
#include "data/data_file_origin.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_lottie.h"
|
||||
#include "styles/style_chat.h"
|
||||
|
@ -43,6 +44,8 @@ constexpr auto kMaxEmojiSizeFixed = 256;
|
|||
constexpr auto kPremiumMultiplier = (1 + 0.245 * 2);
|
||||
constexpr auto kEmojiMultiplier = 3;
|
||||
|
||||
using ClipNotification = ::Media::Clip::Notification;
|
||||
|
||||
[[nodiscard]] QImage CacheDiceImage(
|
||||
const QString &emoji,
|
||||
int index,
|
||||
|
@ -57,6 +60,154 @@ constexpr auto kEmojiMultiplier = 3;
|
|||
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
|
||||
|
||||
Sticker::Sticker(
|
||||
|
@ -69,7 +220,7 @@ Sticker::Sticker(
|
|||
, _data(data)
|
||||
, _replacements(replacements)
|
||||
, _cachingTag(ChatHelpers::StickerLottieSize::MessageHistory)
|
||||
, _lottieOncePlayed(false)
|
||||
, _oncePlayed(false)
|
||||
, _premiumEffectPlayed(false)
|
||||
, _nextLastDiceFrame(false)
|
||||
, _skipPremiumEffect(skipPremiumEffect) {
|
||||
|
@ -82,22 +233,22 @@ Sticker::Sticker(
|
|||
}
|
||||
}
|
||||
if (const auto media = replacing ? replacing->media() : nullptr) {
|
||||
_lottie = media->stickerTakeLottie(_data, _replacements);
|
||||
if (_lottie) {
|
||||
_player = media->stickerTakePlayer(_data, _replacements);
|
||||
if (_player) {
|
||||
//_externalInfo = media->externalLottieInfo();
|
||||
if (hasPremiumEffect() && !_premiumEffectPlayed) {
|
||||
_premiumEffectPlayed = true;
|
||||
_parent->delegate()->elementStartPremium(_parent, replacing);
|
||||
}
|
||||
lottieCreated();
|
||||
playerCreated();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Sticker::~Sticker() {
|
||||
if (_lottie || _dataMedia) {
|
||||
if (_lottie) {
|
||||
unloadLottie();
|
||||
if (_player || _dataMedia) {
|
||||
if (_player) {
|
||||
unloadPlayer();
|
||||
}
|
||||
if (_dataMedia) {
|
||||
_data->owner().keepAlive(base::take(_dataMedia));
|
||||
|
@ -127,21 +278,22 @@ void Sticker::initSize() {
|
|||
_size = EmojiSize();
|
||||
}
|
||||
if (_diceIndex > 0) {
|
||||
[[maybe_unused]] bool result = readyToDrawLottie();
|
||||
[[maybe_unused]] bool result = readyToDrawAnimationFrame();
|
||||
}
|
||||
} else {
|
||||
_size = Size(_data);
|
||||
}
|
||||
_size = DownscaledSize(_size, Size());
|
||||
}
|
||||
|
||||
QSize Sticker::countOptimalSize() {
|
||||
if (_size.isEmpty()) {
|
||||
initSize();
|
||||
}
|
||||
return DownscaledSize(_size, Size());
|
||||
return _size;
|
||||
}
|
||||
|
||||
bool Sticker::readyToDrawLottie() {
|
||||
bool Sticker::readyToDrawAnimationFrame() {
|
||||
if (!_lastDiceFrame.isNull()) {
|
||||
return true;
|
||||
}
|
||||
|
@ -155,10 +307,10 @@ bool Sticker::readyToDrawLottie() {
|
|||
const auto loaded = _dataMedia->loaded();
|
||||
const auto waitingForPremium = hasPremiumEffect()
|
||||
&& _dataMedia->videoThumbnailContent().isEmpty();
|
||||
if (sticker->isLottie() && !_lottie && loaded && !waitingForPremium) {
|
||||
setupLottie();
|
||||
if (!_player && loaded && !waitingForPremium && sticker->isAnimated()) {
|
||||
setupPlayer();
|
||||
}
|
||||
return (_lottie && _lottie->ready());
|
||||
return (_player && _player->ready());
|
||||
}
|
||||
|
||||
QSize Sticker::Size() {
|
||||
|
@ -192,13 +344,13 @@ void Sticker::draw(
|
|||
Painter &p,
|
||||
const PaintContext &context,
|
||||
const QRect &r) {
|
||||
if (!customEmojiPart() && isEmojiSticker()) {
|
||||
if (!customEmojiPart()) {
|
||||
_parent->clearCustomEmojiRepaint();
|
||||
}
|
||||
|
||||
ensureDataMediaCreated();
|
||||
if (readyToDrawLottie()) {
|
||||
paintLottie(p, context, r);
|
||||
if (readyToDrawAnimationFrame()) {
|
||||
paintAnimationFrame(p, context, r);
|
||||
} else if (!_data->sticker()
|
||||
|| (_data->sticker()->isLottie() && _replacements)
|
||||
|| !paintPixmap(p, context, r)) {
|
||||
|
@ -215,23 +367,28 @@ DocumentData *Sticker::document() {
|
|||
}
|
||||
|
||||
void Sticker::stickerClearLoopPlayed() {
|
||||
_lottieOncePlayed = false;
|
||||
_oncePlayed = false;
|
||||
_premiumEffectPlayed = false;
|
||||
}
|
||||
|
||||
void Sticker::paintLottie(
|
||||
void Sticker::paintAnimationFrame(
|
||||
Painter &p,
|
||||
const PaintContext &context,
|
||||
const QRect &r) {
|
||||
auto request = Lottie::FrameRequest();
|
||||
request.box = _size * cIntRetinaFactor();
|
||||
if (context.selected() && !_nextLastDiceFrame) {
|
||||
request.colored = context.st->msgStickerOverlay()->c;
|
||||
}
|
||||
request.mirrorHorizontal = mirrorHorizontal();
|
||||
const auto frame = _lottie
|
||||
? _lottie->frameInfo(request)
|
||||
: Lottie::Animation::FrameInfo();
|
||||
const auto colored = (context.selected() && !_nextLastDiceFrame)
|
||||
? context.st->msgStickerOverlay()->c
|
||||
: QColor(0, 0, 0, 0);
|
||||
const auto paused = /*(_externalInfo.frame >= 0)
|
||||
? (_frameIndex % _externalInfo.count >= _externalInfo.frame)
|
||||
: */_parent->delegate()->elementIsGifPaused();
|
||||
const auto frame = _player
|
||||
? _player->frame(
|
||||
_size,
|
||||
colored,
|
||||
mirrorHorizontal(),
|
||||
context.now,
|
||||
paused)
|
||||
: StickerPlayer::FrameInfo();
|
||||
if (_nextLastDiceFrame) {
|
||||
_nextLastDiceFrame = false;
|
||||
_lastDiceFrame = CacheDiceImage(_diceEmoji, _diceIndex, frame.image);
|
||||
|
@ -256,12 +413,9 @@ void Sticker::paintLottie(
|
|||
return;
|
||||
}
|
||||
|
||||
const auto count = _lottie->information().framesCount;
|
||||
const auto count = _player->framesCount();
|
||||
_frameIndex = frame.index;
|
||||
_framesCount = count;
|
||||
const auto paused = /*(_externalInfo.frame >= 0)
|
||||
? (_frameIndex % _externalInfo.count >= _externalInfo.frame)
|
||||
: */_parent->delegate()->elementIsGifPaused();
|
||||
_nextLastDiceFrame = !paused
|
||||
&& (_diceIndex > 0)
|
||||
&& (_frameIndex + 2 == count);
|
||||
|
@ -274,13 +428,13 @@ void Sticker::paintLottie(
|
|||
const auto lastDiceFrame = (_diceIndex > 0) && atTheEnd();
|
||||
const auto switchToNext = /*(_externalInfo.frame >= 0)
|
||||
|| */!playOnce
|
||||
|| (!lastDiceFrame && (_frameIndex != 0 || !_lottieOncePlayed));
|
||||
|| (!lastDiceFrame && (_frameIndex != 0 || !_oncePlayed));
|
||||
if (!paused
|
||||
&& switchToNext
|
||||
&& _lottie->markFrameShown()
|
||||
&& _player->markFrameShown()
|
||||
&& playOnce
|
||||
&& !_lottieOncePlayed) {
|
||||
_lottieOncePlayed = true;
|
||||
&& !_oncePlayed) {
|
||||
_oncePlayed = true;
|
||||
_parent->delegate()->elementStartStickerLoop(_parent);
|
||||
}
|
||||
checkPremiumEffectStart();
|
||||
|
@ -419,10 +573,10 @@ void Sticker::refreshLink() {
|
|||
}
|
||||
|
||||
void Sticker::emojiStickerClicked() {
|
||||
if (_lottie) {
|
||||
if (_player) {
|
||||
_parent->delegate()->elementStartInteraction(_parent);
|
||||
}
|
||||
_lottieOncePlayed = false;
|
||||
_oncePlayed = false;
|
||||
_parent->history()->owner().requestViewRepaint(_parent);
|
||||
}
|
||||
|
||||
|
@ -464,17 +618,26 @@ void Sticker::setCustomEmojiPart(
|
|||
_cachingTag = tag;
|
||||
}
|
||||
|
||||
void Sticker::setupLottie() {
|
||||
void Sticker::setupPlayer() {
|
||||
Expects(_dataMedia != nullptr);
|
||||
|
||||
_lottie = ChatHelpers::LottiePlayerFromDocument(
|
||||
_dataMedia.get(),
|
||||
_replacements,
|
||||
_cachingTag,
|
||||
countOptimalSize() * style::DevicePixelRatio(),
|
||||
Lottie::Quality::High);
|
||||
if (_data->sticker()->isLottie()) {
|
||||
_player = std::make_unique<LottiePlayer>(
|
||||
ChatHelpers::LottiePlayerFromDocument(
|
||||
_dataMedia.get(),
|
||||
_replacements,
|
||||
_cachingTag,
|
||||
countOptimalSize() * style::DevicePixelRatio(),
|
||||
Lottie::Quality::High));
|
||||
} else if (_data->sticker()->isWebm()) {
|
||||
_player = std::make_unique<WebmPlayer>(
|
||||
_dataMedia->owner()->location(),
|
||||
_dataMedia->bytes(),
|
||||
countOptimalSize());
|
||||
}
|
||||
|
||||
checkPremiumEffectStart();
|
||||
lottieCreated();
|
||||
playerCreated();
|
||||
}
|
||||
|
||||
void Sticker::checkPremiumEffectStart() {
|
||||
|
@ -484,51 +647,42 @@ void Sticker::checkPremiumEffectStart() {
|
|||
}
|
||||
}
|
||||
|
||||
void Sticker::lottieCreated() {
|
||||
Expects(_lottie != nullptr);
|
||||
void Sticker::playerCreated() {
|
||||
Expects(_player != nullptr);
|
||||
|
||||
_parent->history()->owner().registerHeavyViewPart(_parent);
|
||||
|
||||
_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);
|
||||
_player->setRepaintCallback([=] { _parent->customEmojiRepaint(); });
|
||||
}
|
||||
|
||||
bool Sticker::hasHeavyPart() const {
|
||||
return _lottie || _dataMedia;
|
||||
return _player || _dataMedia;
|
||||
}
|
||||
|
||||
void Sticker::unloadHeavyPart() {
|
||||
unloadLottie();
|
||||
unloadPlayer();
|
||||
_dataMedia = nullptr;
|
||||
}
|
||||
|
||||
void Sticker::unloadLottie() {
|
||||
if (!_lottie) {
|
||||
void Sticker::unloadPlayer() {
|
||||
if (!_player) {
|
||||
return;
|
||||
}
|
||||
if (_diceIndex > 0 && _lastDiceFrame.isNull()) {
|
||||
_nextLastDiceFrame = false;
|
||||
_lottieOncePlayed = false;
|
||||
_oncePlayed = false;
|
||||
}
|
||||
_lottie = nullptr;
|
||||
_player = nullptr;
|
||||
if (hasPremiumEffect()) {
|
||||
_parent->delegate()->elementCancelPremium(_parent);
|
||||
}
|
||||
_parent->checkHeavyPart();
|
||||
}
|
||||
|
||||
std::unique_ptr<Lottie::SinglePlayer> Sticker::stickerTakeLottie(
|
||||
std::unique_ptr<StickerPlayer> Sticker::stickerTakePlayer(
|
||||
not_null<DocumentData*> data,
|
||||
const Lottie::ColorReplacements *replacements) {
|
||||
return (data == _data && replacements == _replacements)
|
||||
? std::move(_lottie)
|
||||
? std::move(_player)
|
||||
: nullptr;
|
||||
}
|
||||
|
||||
|
|
|
@ -30,6 +30,26 @@ enum class StickerLottieSize : uint8;
|
|||
|
||||
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
|
||||
: public UnwrappedMedia::Content
|
||||
, public base::has_weak_ptr {
|
||||
|
@ -52,7 +72,7 @@ public:
|
|||
|
||||
DocumentData *document() override;
|
||||
void stickerClearLoopPlayed() override;
|
||||
std::unique_ptr<Lottie::SinglePlayer> stickerTakeLottie(
|
||||
std::unique_ptr<StickerPlayer> stickerTakePlayer(
|
||||
not_null<DocumentData*> data,
|
||||
const Lottie::ColorReplacements *replacements) override;
|
||||
|
||||
|
@ -83,7 +103,7 @@ public:
|
|||
? std::make_optional(_framesCount)
|
||||
: std::nullopt;
|
||||
}
|
||||
[[nodiscard]] bool readyToDrawLottie();
|
||||
[[nodiscard]] bool readyToDrawAnimationFrame();
|
||||
|
||||
[[nodiscard]] static QSize Size();
|
||||
[[nodiscard]] static QSize Size(not_null<DocumentData*> document);
|
||||
|
@ -99,7 +119,10 @@ private:
|
|||
[[nodiscard]] bool hasPremiumEffect() const;
|
||||
[[nodiscard]] bool customEmojiPart() 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);
|
||||
void paintPath(Painter &p, const PaintContext &context, const QRect &r);
|
||||
[[nodiscard]] QPixmap paintedPixmap(const PaintContext &context) const;
|
||||
|
@ -108,9 +131,9 @@ private:
|
|||
void ensureDataMediaCreated() const;
|
||||
void dataMediaCreated() const;
|
||||
|
||||
void setupLottie();
|
||||
void lottieCreated();
|
||||
void unloadLottie();
|
||||
void setupPlayer();
|
||||
void playerCreated();
|
||||
void unloadPlayer();
|
||||
void emojiStickerClicked();
|
||||
void premiumStickerClicked();
|
||||
void checkPremiumEffectStart();
|
||||
|
@ -119,7 +142,7 @@ private:
|
|||
const not_null<Element*> _parent;
|
||||
const not_null<DocumentData*> _data;
|
||||
const Lottie::ColorReplacements *_replacements = nullptr;
|
||||
std::unique_ptr<Lottie::SinglePlayer> _lottie;
|
||||
std::unique_ptr<StickerPlayer> _player;
|
||||
mutable std::shared_ptr<Data::DocumentMedia> _dataMedia;
|
||||
ClickHandlerPtr _link;
|
||||
QSize _size;
|
||||
|
@ -130,13 +153,11 @@ private:
|
|||
mutable int _frameIndex = -1;
|
||||
mutable int _framesCount = -1;
|
||||
ChatHelpers::StickerLottieSize _cachingTag = {};
|
||||
mutable bool _lottieOncePlayed : 1;
|
||||
mutable bool _oncePlayed : 1;
|
||||
mutable bool _premiumEffectPlayed : 1;
|
||||
mutable bool _nextLastDiceFrame : 1;
|
||||
bool _skipPremiumEffect : 1;
|
||||
|
||||
rpl::lifetime _lifetime;
|
||||
|
||||
};
|
||||
|
||||
} // namespace HistoryView
|
||||
|
|
|
@ -190,13 +190,13 @@ void Gif::paint(Painter &p, const QRect &clip, const PaintContext *context) cons
|
|||
_thumb = pixmap;
|
||||
_thumbGood = true;
|
||||
}
|
||||
p.drawPixmap(r.topLeft(), pixmap);
|
||||
p.drawImage(r.topLeft(), pixmap);
|
||||
} else {
|
||||
prepareThumbnail(r.size(), frame);
|
||||
if (_thumb.isNull()) {
|
||||
p.fillRect(r, st::overviewPhotoBg);
|
||||
} else {
|
||||
p.drawPixmap(r.topLeft(), _thumb);
|
||||
p.drawImage(r.topLeft(), _thumb);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -340,7 +340,7 @@ void Gif::validateThumbnail(
|
|||
.options = (Images::Option::TransparentBackground
|
||||
| (good ? Images::Option() : Images::Option::Blur)),
|
||||
.outer = size,
|
||||
});
|
||||
}).toImage();
|
||||
}
|
||||
|
||||
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,
|
||||
.keepAlpha = true,
|
||||
}, context->paused ? 0 : context->ms);
|
||||
p.drawPixmap(
|
||||
p.drawImage(
|
||||
(st::stickerPanSize.width() - size.width()) / 2,
|
||||
(st::stickerPanSize.height() - size.width()) / 2,
|
||||
frame);
|
||||
|
@ -1519,7 +1519,7 @@ void Game::paint(Painter &p, const QRect &clip, const PaintContext *context) con
|
|||
_thumb = pixmap;
|
||||
_thumbGood = true;
|
||||
}
|
||||
p.drawPixmapLeft(rthumb.topLeft(), _width, pixmap);
|
||||
p.drawImage(rthumb.topLeft(), pixmap);
|
||||
thumbDisplayed = true;
|
||||
}
|
||||
}
|
||||
|
@ -1529,7 +1529,7 @@ void Game::paint(Painter &p, const QRect &clip, const PaintContext *context) con
|
|||
if (_thumb.isNull()) {
|
||||
p.fillRect(rthumb, st::overviewPhotoBg);
|
||||
} 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
|
||||
| (good ? Images::Option() : Images::Option::Blur)),
|
||||
.outer = size,
|
||||
});
|
||||
}).toImage();
|
||||
}
|
||||
|
||||
bool Game::isRadialAnimation() const {
|
||||
|
|
|
@ -126,7 +126,7 @@ private:
|
|||
|
||||
Media::Clip::ReaderPointer _gif;
|
||||
ClickHandlerPtr _delete;
|
||||
mutable QPixmap _thumb;
|
||||
mutable QImage _thumb;
|
||||
mutable bool _thumbGood = false;
|
||||
|
||||
mutable std::shared_ptr<Data::DocumentMedia> _dataMedia;
|
||||
|
@ -419,7 +419,7 @@ private:
|
|||
Media::Clip::ReaderPointer _gif;
|
||||
mutable std::shared_ptr<Data::PhotoMedia> _photoMedia;
|
||||
mutable std::shared_ptr<Data::DocumentMedia> _documentMedia;
|
||||
mutable QPixmap _thumb;
|
||||
mutable QImage _thumb;
|
||||
mutable bool _thumbGood = false;
|
||||
mutable std::unique_ptr<Ui::RadialAnimation> _radial;
|
||||
Ui::Text::String _title, _description;
|
||||
|
|
|
@ -91,6 +91,7 @@ ReaderImplementation::ReadResult FFMpegReaderImplementation::readNextFrame() {
|
|||
_frameMs = 0;
|
||||
_lastReadVideoMs = _lastReadAudioMs = 0;
|
||||
_skippedInvalidDataPackets = 0;
|
||||
_frameIndex = -1;
|
||||
|
||||
continue;
|
||||
} else if (res != AVERROR(EAGAIN)) {
|
||||
|
@ -162,6 +163,7 @@ void FFMpegReaderImplementation::processReadFrame() {
|
|||
|
||||
_hadFrame = _frameRead = true;
|
||||
_frameTime += _currentFrameDelay;
|
||||
++_frameIndex;
|
||||
}
|
||||
|
||||
ReaderImplementation::ReadResult FFMpegReaderImplementation::readFramesTill(crl::time frameMs, crl::time systemMs) {
|
||||
|
@ -200,10 +202,15 @@ crl::time FFMpegReaderImplementation::durationMs() const {
|
|||
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);
|
||||
_frameRead = false;
|
||||
|
||||
_frameRead = false;
|
||||
index = _frameIndex;
|
||||
if (!_width || !_height) {
|
||||
_width = _frame->width;
|
||||
_height = _frame->height;
|
||||
|
|
|
@ -33,7 +33,11 @@ public:
|
|||
crl::time frameRealTime() 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;
|
||||
|
||||
|
@ -85,6 +89,7 @@ private:
|
|||
AVCodecContext *_codecContext = nullptr;
|
||||
int _streamId = 0;
|
||||
FFmpeg::FramePointer _frame;
|
||||
int _frameIndex = -1;
|
||||
bool _opened = false;
|
||||
bool _hadFrame = false;
|
||||
bool _frameRead = false;
|
||||
|
|
|
@ -42,7 +42,11 @@ public:
|
|||
virtual crl::time framePresentationTime() const = 0;
|
||||
|
||||
// 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;
|
||||
|
||||
|
|
|
@ -35,11 +35,21 @@ constexpr auto kClipThreadsCount = 8;
|
|||
constexpr auto kAverageGifSize = 320 * 240;
|
||||
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 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);
|
||||
if (!needResize && !needOuterFill && !hasAlpha && !needRounding) {
|
||||
const auto colorizing = (request.colored.alpha() != 0);
|
||||
if (!needResize
|
||||
&& !needOuterFill
|
||||
&& !hasAlpha
|
||||
&& !needRounding
|
||||
&& !colorizing) {
|
||||
return original;
|
||||
}
|
||||
|
||||
|
@ -89,13 +99,12 @@ QImage PrepareFrameImage(const FrameRequest &request, const QImage &original, bo
|
|||
request.radius,
|
||||
request.corners);
|
||||
}
|
||||
if (colorizing) {
|
||||
cache = Images::Colored(std::move(cache), request.colored);
|
||||
}
|
||||
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
|
||||
|
||||
enum class ProcessResult {
|
||||
|
@ -254,15 +263,18 @@ Reader::Frame *Reader::frameToWriteNext(bool checkNotWriting, int32 *index) cons
|
|||
return _frames + i;
|
||||
}
|
||||
|
||||
void Reader::moveToNextShow() const {
|
||||
int32 step = _step.loadAcquire();
|
||||
bool Reader::moveToNextShow() const {
|
||||
const auto step = _step.loadAcquire();
|
||||
if (step == kWaitingForDimensionsStep) {
|
||||
} else if (step == kWaitingForRequestStep) {
|
||||
_step.storeRelease(kWaitingForFirstFrameStep);
|
||||
return true;
|
||||
} else if (step == kWaitingForFirstFrameStep) {
|
||||
} else if (!(step % 2)) {
|
||||
_step.storeRelease(step + 1);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void Reader::moveToNextWrite() const {
|
||||
|
@ -311,7 +323,7 @@ void Reader::start(FrameRequest request) {
|
|||
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()
|
||||
? request.outer
|
||||
: request.frame).isEmpty());
|
||||
|
@ -346,31 +358,32 @@ QPixmap Reader::current(FrameRequest request, crl::time now) {
|
|||
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;
|
||||
if (frame->prepared.size() != size
|
||||
|| frame->preparedColored != request.colored) {
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
return { frame->prepared, frame->index };
|
||||
}
|
||||
|
||||
bool Reader::ready() const {
|
||||
|
@ -478,7 +491,7 @@ public:
|
|||
if (reader->start(internal::ReaderImplementation::Mode::Silent, firstFramePositionMs)) {
|
||||
auto firstFrameReadResult = reader->readFramesTill(-1, ms);
|
||||
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()->positionMs = _seekPositionMs;
|
||||
|
@ -495,7 +508,7 @@ public:
|
|||
} else if (readResult != internal::ReaderImplementation::ReadResult::Success) { // Read the first frame.
|
||||
return error();
|
||||
}
|
||||
if (!_implementation->renderFrame(frame()->original, frame()->alpha, QSize())) {
|
||||
if (!_implementation->renderFrame(frame()->original, frame()->alpha, frame()->index, QSize())) {
|
||||
return error();
|
||||
}
|
||||
frame()->positionMs = _implementation->frameRealTime();
|
||||
|
@ -555,12 +568,17 @@ public:
|
|||
bool renderFrame() {
|
||||
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;
|
||||
}
|
||||
frame()->original.setDevicePixelRatio(_request.factor);
|
||||
frame()->pix = QPixmap();
|
||||
frame()->pix = PrepareFrame(_request, frame()->original, frame()->alpha, frame()->cache);
|
||||
frame()->prepared = QImage();
|
||||
frame()->prepared = PrepareFrame(
|
||||
_request,
|
||||
frame()->original,
|
||||
frame()->alpha,
|
||||
frame()->cache);
|
||||
frame()->preparedColored = _request.colored;
|
||||
frame()->when = _nextFrameWhen;
|
||||
frame()->positionMs = _nextFramePositionMs;
|
||||
return true;
|
||||
|
@ -638,8 +656,11 @@ private:
|
|||
|
||||
FrameRequest _request;
|
||||
struct Frame {
|
||||
QPixmap pix;
|
||||
QImage original, cache;
|
||||
QImage prepared;
|
||||
QColor preparedColored = QColor(0, 0, 0, 0);
|
||||
QImage original;
|
||||
QImage cache;
|
||||
int index = 0;
|
||||
bool alpha = true;
|
||||
crl::time when = 0;
|
||||
|
||||
|
@ -780,8 +801,10 @@ bool Manager::handleProcessResult(ReaderPrivate *reader, ProcessResult result, c
|
|||
Assert(reader->_frame >= 0);
|
||||
auto frame = it.key()->_frames + reader->_frame;
|
||||
frame->clear();
|
||||
frame->pix = reader->frame()->pix;
|
||||
frame->prepared = reader->frame()->prepared;
|
||||
frame->preparedColored = reader->frame()->preparedColored;
|
||||
frame->original = reader->frame()->original;
|
||||
frame->index = reader->frame()->index;
|
||||
frame->displayed.storeRelease(0);
|
||||
frame->positionMs = reader->frame()->positionMs;
|
||||
if (result == ProcessResult::Started) {
|
||||
|
@ -959,10 +982,11 @@ Ui::PreparedFileInformation::Video PrepareForSending(const QString &fname, const
|
|||
// return result;
|
||||
// }
|
||||
//}
|
||||
auto index = 0;
|
||||
auto hasAlpha = false;
|
||||
auto readResult = reader->readFramesTill(-1, crl::now());
|
||||
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) {
|
||||
result.thumbnail = Images::Opaque(std::move(result.thumbnail));
|
||||
}
|
||||
|
|
|
@ -27,14 +27,16 @@ enum class State {
|
|||
};
|
||||
|
||||
struct FrameRequest {
|
||||
bool valid() const {
|
||||
[[nodiscard]] bool valid() const {
|
||||
return factor > 0;
|
||||
}
|
||||
|
||||
QSize frame;
|
||||
QSize outer;
|
||||
int factor = 0;
|
||||
ImageRoundRadius radius = ImageRoundRadius::None;
|
||||
RectParts corners = RectPart::AllCorners;
|
||||
QColor colored = QColor(0, 0, 0, 0);
|
||||
bool keepAlpha = false;
|
||||
};
|
||||
|
||||
|
@ -74,14 +76,27 @@ public:
|
|||
Notification notification);
|
||||
|
||||
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()) {
|
||||
auto result = QPixmap::fromImage(frame->original);
|
||||
auto result = frame->original;
|
||||
result.detach();
|
||||
return result;
|
||||
}
|
||||
return QPixmap();
|
||||
return QImage();
|
||||
}
|
||||
bool moveToNextFrame() {
|
||||
return moveToNextShow();
|
||||
}
|
||||
[[nodiscard]] bool currentDisplayed() const {
|
||||
const auto frame = frameToShow();
|
||||
|
@ -130,13 +145,17 @@ private:
|
|||
mutable QAtomicInt _step = kWaitingForDimensionsStep;
|
||||
struct Frame {
|
||||
void clear() {
|
||||
pix = QPixmap();
|
||||
prepared = QImage();
|
||||
preparedColored = QColor(0, 0, 0, 0);
|
||||
original = QImage();
|
||||
}
|
||||
QPixmap pix;
|
||||
|
||||
QImage prepared;
|
||||
QColor preparedColored = QColor(0, 0, 0, 0);
|
||||
QImage original;
|
||||
FrameRequest request;
|
||||
QAtomicInt displayed = 0;
|
||||
int index = 0;
|
||||
|
||||
// Should be counted from the end,
|
||||
// so that positionMs <= _durationMs.
|
||||
|
@ -146,7 +165,7 @@ private:
|
|||
Frame *frameToShow(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;
|
||||
void moveToNextShow() const;
|
||||
bool moveToNextShow() const;
|
||||
void moveToNextWrite() const;
|
||||
|
||||
QAtomicInt _autoPausedGif = 0;
|
||||
|
|
|
@ -1974,7 +1974,7 @@ void Gif::validateThumbnail(
|
|||
{
|
||||
.options = (good ? Images::Option() : Images::Option::Blur),
|
||||
.outer = size,
|
||||
});
|
||||
}).toImage();
|
||||
}
|
||||
|
||||
void Gif::prepareThumbnail(QSize size, QSize frame) {
|
||||
|
@ -2031,13 +2031,13 @@ void Gif::paint(
|
|||
_thumb = pixmap;
|
||||
_thumbGood = true;
|
||||
}
|
||||
p.drawPixmap(r.topLeft(), pixmap);
|
||||
p.drawImage(r.topLeft(), pixmap);
|
||||
} else {
|
||||
prepareThumbnail(r.size(), frame);
|
||||
if (_thumb.isNull()) {
|
||||
p.fillRect(r, st::overviewPhotoBg);
|
||||
} else {
|
||||
p.drawPixmap(r.topLeft(), _thumb);
|
||||
p.drawImage(r.topLeft(), _thumb);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -255,7 +255,7 @@ private:
|
|||
mutable std::shared_ptr<Data::DocumentMedia> _dataMedia;
|
||||
StatusText _status;
|
||||
|
||||
QPixmap _thumb;
|
||||
QImage _thumb;
|
||||
bool _thumbGood = false;
|
||||
|
||||
};
|
||||
|
|
|
@ -80,7 +80,7 @@ bool SingleMediaPreview::tryPaintAnimation(Painter &p) {
|
|||
const auto frame = _gifPreview->current({
|
||||
.frame = QSize(previewWidth(), previewHeight()),
|
||||
}, paused ? 0 : crl::now());
|
||||
p.drawPixmap(previewLeft(), previewTop(), frame);
|
||||
p.drawImage(previewLeft(), previewTop(), frame);
|
||||
return true;
|
||||
} else if (_lottiePreview && _lottiePreview->ready()) {
|
||||
const auto frame = _lottiePreview->frame();
|
||||
|
|
|
@ -381,9 +381,9 @@ QPixmap MediaPreviewWidget::currentImage() const {
|
|||
if (gif && gif->started()) {
|
||||
const auto paused = _controller->isGifPausedAtLeastFor(
|
||||
Window::GifPauseReason::MediaPreview);
|
||||
return gif->current(
|
||||
return QPixmap::fromImage(gif->current(
|
||||
{ .frame = currentDimensions(), .keepAlpha = webm },
|
||||
paused ? 0 : crl::now());
|
||||
paused ? 0 : crl::now()), Qt::ColorOnly);
|
||||
}
|
||||
if (_cacheStatus != CacheThumbLoaded
|
||||
&& _document->hasThumbnail()) {
|
||||
|
|
Loading…
Add table
Reference in a new issue