Support webm stickers in StickerSetBox.
This commit is contained in:
parent
d96a8d028a
commit
b4eb25de58
|
@ -32,6 +32,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "lottie/lottie_multi_player.h"
|
#include "lottie/lottie_multi_player.h"
|
||||||
#include "lottie/lottie_animation.h"
|
#include "lottie/lottie_animation.h"
|
||||||
#include "chat_helpers/stickers_lottie.h"
|
#include "chat_helpers/stickers_lottie.h"
|
||||||
|
#include "media/clip/media_clip_reader.h"
|
||||||
#include "window/window_session_controller.h"
|
#include "window/window_session_controller.h"
|
||||||
#include "base/unixtime.h"
|
#include "base/unixtime.h"
|
||||||
#include "main/main_session.h"
|
#include "main/main_session.h"
|
||||||
|
@ -51,6 +52,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
constexpr auto kStickersPanelPerRow = 5;
|
constexpr auto kStickersPanelPerRow = 5;
|
||||||
|
constexpr auto kMinRepaintDelay = crl::time(33);
|
||||||
|
constexpr auto kMinAfterScrollDelay = crl::time(33);
|
||||||
|
|
||||||
using Data::StickersSet;
|
using Data::StickersSet;
|
||||||
using Data::StickersPack;
|
using Data::StickersPack;
|
||||||
|
@ -99,7 +102,8 @@ private:
|
||||||
struct Element {
|
struct Element {
|
||||||
not_null<DocumentData*> document;
|
not_null<DocumentData*> document;
|
||||||
std::shared_ptr<Data::DocumentMedia> documentMedia;
|
std::shared_ptr<Data::DocumentMedia> documentMedia;
|
||||||
Lottie::Animation *animated = nullptr;
|
Lottie::Animation *lottie = nullptr;
|
||||||
|
Media::Clip::ReaderPointer webm;
|
||||||
Ui::Animations::Simple overAnimation;
|
Ui::Animations::Simple overAnimation;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -107,8 +111,18 @@ private:
|
||||||
|
|
||||||
QSize boundingBoxSize() const;
|
QSize boundingBoxSize() const;
|
||||||
|
|
||||||
void paintSticker(Painter &p, int index, QPoint position) const;
|
void paintSticker(
|
||||||
|
Painter &p,
|
||||||
|
int index,
|
||||||
|
QPoint position,
|
||||||
|
bool paused,
|
||||||
|
crl::time now) const;
|
||||||
void setupLottie(int index);
|
void setupLottie(int index);
|
||||||
|
void setupWebm(int index);
|
||||||
|
void clipCallback(
|
||||||
|
Media::Clip::Notification notification,
|
||||||
|
not_null<DocumentData*> document,
|
||||||
|
int index);
|
||||||
|
|
||||||
void updateSelected();
|
void updateSelected();
|
||||||
void setSelected(int selected);
|
void setSelected(int selected);
|
||||||
|
@ -123,6 +137,8 @@ private:
|
||||||
not_null<Lottie::MultiPlayer*> getLottiePlayer();
|
not_null<Lottie::MultiPlayer*> getLottiePlayer();
|
||||||
|
|
||||||
void showPreview();
|
void showPreview();
|
||||||
|
void updateItems();
|
||||||
|
void repaintItems(crl::time now = 0);
|
||||||
|
|
||||||
not_null<Window::SessionController*> _controller;
|
not_null<Window::SessionController*> _controller;
|
||||||
MTP::Sender _api;
|
MTP::Sender _api;
|
||||||
|
@ -142,6 +158,12 @@ private:
|
||||||
|
|
||||||
const std::unique_ptr<Ui::PathShiftGradient> _pathGradient;
|
const std::unique_ptr<Ui::PathShiftGradient> _pathGradient;
|
||||||
|
|
||||||
|
int _visibleTop = 0;
|
||||||
|
int _visibleBottom = 0;
|
||||||
|
crl::time _lastScrolledAt = 0;
|
||||||
|
crl::time _lastUpdatedAt = 0;
|
||||||
|
base::Timer _updateItemsTimer;
|
||||||
|
|
||||||
StickerSetIdentifier _input;
|
StickerSetIdentifier _input;
|
||||||
|
|
||||||
mtpRequestId _installRequest = 0;
|
mtpRequestId _installRequest = 0;
|
||||||
|
@ -370,9 +392,12 @@ StickerSetBox::Inner::Inner(
|
||||||
, _pathGradient(std::make_unique<Ui::PathShiftGradient>(
|
, _pathGradient(std::make_unique<Ui::PathShiftGradient>(
|
||||||
st::windowBgRipple,
|
st::windowBgRipple,
|
||||||
st::windowBgOver,
|
st::windowBgOver,
|
||||||
[=] { update(); }))
|
[=] { repaintItems(); }))
|
||||||
|
, _updateItemsTimer([=] { updateItems(); })
|
||||||
, _input(set)
|
, _input(set)
|
||||||
, _previewTimer([=] { showPreview(); }) {
|
, _previewTimer([=] { showPreview(); }) {
|
||||||
|
setAttribute(Qt::WA_OpaquePaintEvent);
|
||||||
|
|
||||||
_api.request(MTPmessages_GetStickerSet(
|
_api.request(MTPmessages_GetStickerSet(
|
||||||
Data::InputStickerSet(_input),
|
Data::InputStickerSet(_input),
|
||||||
MTP_int(0) // hash
|
MTP_int(0) // hash
|
||||||
|
@ -387,7 +412,7 @@ StickerSetBox::Inner::Inner(
|
||||||
|
|
||||||
_controller->session().downloaderTaskFinished(
|
_controller->session().downloaderTaskFinished(
|
||||||
) | rpl::start_with_next([=] {
|
) | rpl::start_with_next([=] {
|
||||||
update();
|
updateItems();
|
||||||
}, lifetime());
|
}, lifetime());
|
||||||
|
|
||||||
setMouseTracking(true);
|
setMouseTracking(true);
|
||||||
|
@ -735,7 +760,7 @@ not_null<Lottie::MultiPlayer*> StickerSetBox::Inner::getLottiePlayer() {
|
||||||
Lottie::MakeFrameRenderer());
|
Lottie::MakeFrameRenderer());
|
||||||
_lottiePlayer->updates(
|
_lottiePlayer->updates(
|
||||||
) | rpl::start_with_next([=] {
|
) | rpl::start_with_next([=] {
|
||||||
update();
|
updateItems();
|
||||||
}, lifetime());
|
}, lifetime());
|
||||||
}
|
}
|
||||||
return _lottiePlayer.get();
|
return _lottiePlayer.get();
|
||||||
|
@ -756,6 +781,7 @@ int32 StickerSetBox::Inner::stickerFromGlobalPos(const QPoint &p) const {
|
||||||
void StickerSetBox::Inner::paintEvent(QPaintEvent *e) {
|
void StickerSetBox::Inner::paintEvent(QPaintEvent *e) {
|
||||||
Painter p(this);
|
Painter p(this);
|
||||||
|
|
||||||
|
p.fillRect(e->rect(), st::boxBg);
|
||||||
if (_elements.empty()) {
|
if (_elements.empty()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -764,6 +790,9 @@ void StickerSetBox::Inner::paintEvent(QPaintEvent *e) {
|
||||||
|
|
||||||
_pathGradient->startFrame(0, width(), width() / 2);
|
_pathGradient->startFrame(0, width(), width() / 2);
|
||||||
|
|
||||||
|
const auto now = crl::now();
|
||||||
|
const auto paused = _controller->isGifPausedAtLeastFor(
|
||||||
|
Window::GifPauseReason::Layer);
|
||||||
for (int32 i = from; i < to; ++i) {
|
for (int32 i = from; i < to; ++i) {
|
||||||
for (int32 j = 0; j < kStickersPanelPerRow; ++j) {
|
for (int32 j = 0; j < kStickersPanelPerRow; ++j) {
|
||||||
int32 index = i * kStickersPanelPerRow + j;
|
int32 index = i * kStickersPanelPerRow + j;
|
||||||
|
@ -771,16 +800,12 @@ void StickerSetBox::Inner::paintEvent(QPaintEvent *e) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
const auto pos = QPoint(st::stickersPadding.left() + j * st::stickersSize.width(), st::stickersPadding.top() + i * st::stickersSize.height());
|
const auto pos = QPoint(st::stickersPadding.left() + j * st::stickersSize.width(), st::stickersPadding.top() + i * st::stickersSize.height());
|
||||||
paintSticker(p, index, pos);
|
paintSticker(p, index, pos, paused, now);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_lottiePlayer) {
|
if (_lottiePlayer && !paused) {
|
||||||
const auto paused = _controller->isGifPausedAtLeastFor(
|
_lottiePlayer->markFrameShown();
|
||||||
Window::GifPauseReason::Layer);
|
|
||||||
if (!paused) {
|
|
||||||
_lottiePlayer->markFrameShown();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -793,6 +818,12 @@ QSize StickerSetBox::Inner::boundingBoxSize() const {
|
||||||
void StickerSetBox::Inner::visibleTopBottomUpdated(
|
void StickerSetBox::Inner::visibleTopBottomUpdated(
|
||||||
int visibleTop,
|
int visibleTop,
|
||||||
int visibleBottom) {
|
int visibleBottom) {
|
||||||
|
if (_visibleTop != visibleTop || _visibleBottom != visibleBottom) {
|
||||||
|
_visibleTop = visibleTop;
|
||||||
|
_visibleBottom = visibleBottom;
|
||||||
|
_lastScrolledAt = crl::now();
|
||||||
|
update();
|
||||||
|
}
|
||||||
const auto pauseInRows = [&](int fromRow, int tillRow) {
|
const auto pauseInRows = [&](int fromRow, int tillRow) {
|
||||||
Expects(fromRow <= tillRow);
|
Expects(fromRow <= tillRow);
|
||||||
|
|
||||||
|
@ -802,8 +833,10 @@ void StickerSetBox::Inner::visibleTopBottomUpdated(
|
||||||
if (index >= _elements.size()) {
|
if (index >= _elements.size()) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (const auto animated = _elements[index].animated) {
|
if (const auto lottie = _elements[index].lottie) {
|
||||||
_lottiePlayer->pause(animated);
|
_lottiePlayer->pause(lottie);
|
||||||
|
} else if (auto &webm = _elements[index].webm) {
|
||||||
|
webm = nullptr;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -834,17 +867,63 @@ void StickerSetBox::Inner::visibleTopBottomUpdated(
|
||||||
void StickerSetBox::Inner::setupLottie(int index) {
|
void StickerSetBox::Inner::setupLottie(int index) {
|
||||||
auto &element = _elements[index];
|
auto &element = _elements[index];
|
||||||
|
|
||||||
element.animated = ChatHelpers::LottieAnimationFromDocument(
|
element.lottie = ChatHelpers::LottieAnimationFromDocument(
|
||||||
getLottiePlayer(),
|
getLottiePlayer(),
|
||||||
element.documentMedia.get(),
|
element.documentMedia.get(),
|
||||||
ChatHelpers::StickerLottieSize::StickerSet,
|
ChatHelpers::StickerLottieSize::StickerSet,
|
||||||
boundingBoxSize() * cIntRetinaFactor());
|
boundingBoxSize() * cIntRetinaFactor());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void StickerSetBox::Inner::setupWebm(int index) {
|
||||||
|
auto &element = _elements[index];
|
||||||
|
|
||||||
|
const auto document = element.document;
|
||||||
|
auto callback = [=](Media::Clip::Notification notification) {
|
||||||
|
clipCallback(notification, document, index);
|
||||||
|
};
|
||||||
|
element.webm = Media::Clip::MakeReader(
|
||||||
|
element.documentMedia->owner()->location(),
|
||||||
|
element.documentMedia->bytes(),
|
||||||
|
std::move(callback));
|
||||||
|
}
|
||||||
|
|
||||||
|
void StickerSetBox::Inner::clipCallback(
|
||||||
|
Media::Clip::Notification notification,
|
||||||
|
not_null<DocumentData*> document,
|
||||||
|
int index) {
|
||||||
|
const auto i = (index < _elements.size()
|
||||||
|
&& _elements[index].document == document)
|
||||||
|
? (_elements.begin() + index)
|
||||||
|
: ranges::find(_elements, document, &Element::document);
|
||||||
|
if (i == end(_elements)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
using namespace Media::Clip;
|
||||||
|
switch (notification) {
|
||||||
|
case Notification::Reinit: {
|
||||||
|
auto &webm = i->webm;
|
||||||
|
if (webm->state() == State::Error) {
|
||||||
|
webm.setBad();
|
||||||
|
} else if (webm->ready() && !webm->started()) {
|
||||||
|
const auto size = ChatHelpers::ComputeStickerSize(
|
||||||
|
i->document,
|
||||||
|
boundingBoxSize());
|
||||||
|
webm->start({ .frame = size, .keepAlpha = true });
|
||||||
|
}
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case Notification::Repaint: break;
|
||||||
|
}
|
||||||
|
|
||||||
|
updateItems();
|
||||||
|
}
|
||||||
|
|
||||||
void StickerSetBox::Inner::paintSticker(
|
void StickerSetBox::Inner::paintSticker(
|
||||||
Painter &p,
|
Painter &p,
|
||||||
int index,
|
int index,
|
||||||
QPoint position) const {
|
QPoint position,
|
||||||
|
bool paused,
|
||||||
|
crl::time now) const {
|
||||||
if (const auto over = _elements[index].overAnimation.value((index == _selected) ? 1. : 0.)) {
|
if (const auto over = _elements[index].overAnimation.value((index == _selected) ? 1. : 0.)) {
|
||||||
p.setOpacity(over);
|
p.setOpacity(over);
|
||||||
auto tl = position;
|
auto tl = position;
|
||||||
|
@ -856,47 +935,46 @@ void StickerSetBox::Inner::paintSticker(
|
||||||
const auto &element = _elements[index];
|
const auto &element = _elements[index];
|
||||||
const auto document = element.document;
|
const auto document = element.document;
|
||||||
const auto &media = element.documentMedia;
|
const auto &media = element.documentMedia;
|
||||||
|
const auto sticker = document->sticker();
|
||||||
media->checkStickerSmall();
|
media->checkStickerSmall();
|
||||||
|
|
||||||
const auto isLottie = document->sticker()->isLottie();
|
if (media->loaded()) {
|
||||||
if (isLottie
|
if (sticker->isLottie() && !element.lottie) {
|
||||||
&& !element.animated
|
const_cast<Inner*>(this)->setupLottie(index);
|
||||||
&& media->loaded()) {
|
} else if (sticker->isWebm() && !element.webm) {
|
||||||
const_cast<Inner*>(this)->setupLottie(index);
|
const_cast<Inner*>(this)->setupWebm(index);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
auto w = 1;
|
const auto size = ChatHelpers::ComputeStickerSize(
|
||||||
auto h = 1;
|
document,
|
||||||
if (isLottie && !document->dimensions.isEmpty()) {
|
boundingBoxSize());
|
||||||
const auto request = Lottie::FrameRequest{ boundingBoxSize() * cIntRetinaFactor() };
|
const auto ppos = position + QPoint(
|
||||||
const auto size = request.size(document->dimensions, true) / cIntRetinaFactor();
|
(st::stickersSize.width() - size.width()) / 2,
|
||||||
w = std::max(size.width(), 1);
|
(st::stickersSize.height() - size.height()) / 2);
|
||||||
h = std::max(size.height(), 1);
|
|
||||||
} else {
|
|
||||||
auto coef = qMin((st::stickersSize.width() - st::roundRadiusSmall * 2) / float64(document->dimensions.width()), (st::stickersSize.height() - st::roundRadiusSmall * 2) / float64(document->dimensions.height()));
|
|
||||||
if (coef > 1) coef = 1;
|
|
||||||
w = std::max(qRound(coef * document->dimensions.width()), 1);
|
|
||||||
h = std::max(qRound(coef * document->dimensions.height()), 1);
|
|
||||||
}
|
|
||||||
QPoint ppos = position + QPoint((st::stickersSize.width() - w) / 2, (st::stickersSize.height() - h) / 2);
|
|
||||||
|
|
||||||
if (element.animated && element.animated->ready()) {
|
if (element.lottie && element.lottie->ready()) {
|
||||||
const auto frame = element.animated->frame();
|
const auto frame = element.lottie->frame();
|
||||||
p.drawImage(
|
p.drawImage(
|
||||||
QRect(ppos, frame.size() / cIntRetinaFactor()),
|
QRect(ppos, frame.size() / cIntRetinaFactor()),
|
||||||
frame);
|
frame);
|
||||||
|
|
||||||
_lottiePlayer->unpause(element.animated);
|
_lottiePlayer->unpause(element.lottie);
|
||||||
|
} else if (element.webm && element.webm->started()) {
|
||||||
|
p.drawPixmap(ppos, element.webm->current({
|
||||||
|
.frame = size,
|
||||||
|
.keepAlpha = true,
|
||||||
|
}, paused ? 0 : now));
|
||||||
} else if (const auto image = media->getStickerSmall()) {
|
} else if (const auto image = media->getStickerSmall()) {
|
||||||
p.drawPixmapLeft(
|
p.drawPixmapLeft(
|
||||||
ppos,
|
ppos,
|
||||||
width(),
|
width(),
|
||||||
image->pix(w, h));
|
image->pix(size));
|
||||||
} else {
|
} else {
|
||||||
ChatHelpers::PaintStickerThumbnailPath(
|
ChatHelpers::PaintStickerThumbnailPath(
|
||||||
p,
|
p,
|
||||||
media.get(),
|
media.get(),
|
||||||
QRect(ppos, QSize(w, h)),
|
QRect(ppos, size),
|
||||||
_pathGradient.get());
|
_pathGradient.get());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -965,4 +1043,23 @@ void StickerSetBox::Inner::archiveStickers() {
|
||||||
}).send();
|
}).send();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void StickerSetBox::Inner::updateItems() {
|
||||||
|
const auto now = crl::now();
|
||||||
|
|
||||||
|
const auto delay = std::max(
|
||||||
|
_lastScrolledAt + kMinAfterScrollDelay - now,
|
||||||
|
_lastUpdatedAt + kMinRepaintDelay - now);
|
||||||
|
if (delay <= 0) {
|
||||||
|
repaintItems(now);
|
||||||
|
} else if (!_updateItemsTimer.isActive()
|
||||||
|
|| _updateItemsTimer.remainingTime() > kMinRepaintDelay) {
|
||||||
|
_updateItemsTimer.callOnce(std::max(delay, kMinRepaintDelay));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void StickerSetBox::Inner::repaintItems(crl::time now) {
|
||||||
|
_lastUpdatedAt = now ? now : crl::now();
|
||||||
|
update();
|
||||||
|
}
|
||||||
|
|
||||||
StickerSetBox::Inner::~Inner() = default;
|
StickerSetBox::Inner::~Inner() = default;
|
||||||
|
|
|
@ -936,7 +936,7 @@ void GifsListWidget::updateInlineItems() {
|
||||||
_lastScrolledAt + kMinAfterScrollDelay - now,
|
_lastScrolledAt + kMinAfterScrollDelay - now,
|
||||||
_lastUpdatedAt + kMinRepaintDelay - now);
|
_lastUpdatedAt + kMinRepaintDelay - now);
|
||||||
if (delay <= 0) {
|
if (delay <= 0) {
|
||||||
repaintItems();
|
repaintItems(now);
|
||||||
} else if (!_updateInlineItems.isActive()
|
} else if (!_updateInlineItems.isActive()
|
||||||
|| _updateInlineItems.remainingTime() > kMinRepaintDelay) {
|
|| _updateInlineItems.remainingTime() > kMinRepaintDelay) {
|
||||||
_updateInlineItems.callOnce(std::max(delay, kMinRepaintDelay));
|
_updateInlineItems.callOnce(std::max(delay, kMinRepaintDelay));
|
||||||
|
|
Loading…
Reference in New Issue