mirror of
https://github.com/AyuGram/AyuGramDesktop.git
synced 2025-06-05 06:33:57 +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);
|
_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));
|
||||||
|
|
|
@ -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()));
|
||||||
|
|
|
@ -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));
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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());
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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() {
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
|
@ -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;
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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;
|
||||||
|
|
||||||
|
|
|
@ -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));
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
|
@ -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();
|
||||||
|
|
|
@ -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()) {
|
||||||
|
|
Loading…
Add table
Reference in a new issue