Show premium effects in sticker preview.

This commit is contained in:
John Preston 2022-04-28 13:15:12 +04:00
parent af9a252b64
commit 877be8e6cb
2 changed files with 98 additions and 24 deletions

View File

@ -25,7 +25,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
namespace Window {
namespace {
constexpr int kStickerPreviewEmojiLimit = 10;
constexpr auto kStickerPreviewEmojiLimit = 10;
constexpr auto kPremiumMultiplier = 2.25;
constexpr auto kPremiumDownscale = 1.5;
} // namespace
@ -44,25 +46,34 @@ MediaPreviewWidget::MediaPreviewWidget(
QRect MediaPreviewWidget::updateArea() const {
const auto size = currentDimensions();
return QRect(
QPoint((width() - size.width()) / 2, (height() - size.height()) / 2),
size);
const auto position = QPoint(
(width() - size.width()) / 2,
(height() - size.height()) / 2);
const auto premium = _document && _document->isPremiumSticker();
const auto adjusted = position
- (premium
? QPoint(size.width() - (size.width() / 2), size.height() / 2)
: QPoint());
return QRect(adjusted, size * (premium ? 2 : 1));
}
void MediaPreviewWidget::paintEvent(QPaintEvent *e) {
Painter p(this);
QRect r(e->rect());
const auto image = [&] {
if (!_lottie || !_lottie->ready()) {
return QImage();
}
_lottie->markFrameShown();
return _lottie->frame();
}();
const auto r = e->rect();
const auto factor = cIntRetinaFactor();
const auto dimensions = currentDimensions();
const auto frame = (_lottie && _lottie->ready())
? _lottie->frameInfo({ dimensions * factor })
: Lottie::Animation::FrameInfo();
const auto effect = (_effect && _effect->ready())
? _effect->frameInfo({ dimensions * kPremiumMultiplier * factor })
: Lottie::Animation::FrameInfo();
const auto image = frame.image;
const auto effectImage = effect.image;
const auto pixmap = image.isNull() ? currentImage() : QPixmap();
const auto size = image.isNull() ? pixmap.size() : image.size();
int w = size.width() / cIntRetinaFactor(), h = size.height() / cIntRetinaFactor();
int w = size.width() / factor, h = size.height() / factor;
auto shown = _a_shown.value(_hiding ? 0. : 1.);
if (!_a_shown.animating()) {
if (_hiding) {
@ -76,12 +87,14 @@ void MediaPreviewWidget::paintEvent(QPaintEvent *e) {
// h = qMax(qRound(h * (st::stickerPreviewMin + ((1. - st::stickerPreviewMin) * shown)) / 2.) * 2 + int(h % 2), 1);
}
p.fillRect(r, st::stickerPreviewBg);
const auto position = innerPosition({ w, h });
if (image.isNull()) {
p.drawPixmap((width() - w) / 2, (height() - h) / 2, pixmap);
p.drawPixmap(position, pixmap);
} else {
p.drawImage(
QRect((width() - w) / 2, (height() - h) / 2, w, h),
image);
p.drawImage(QRect(position, QSize(w, h)), image);
}
if (!effectImage.isNull()) {
p.drawImage(outerPosition({ w, h }), effectImage);
}
if (!_emojiList.empty()) {
const auto emojiCount = _emojiList.size();
@ -98,12 +111,39 @@ void MediaPreviewWidget::paintEvent(QPaintEvent *e) {
emojiLeft += _emojiSize + st::stickerEmojiSkip;
}
}
if (!frame.image.isNull() && frame.index <= effect.index) {
_lottie->markFrameShown();
}
if (!effect.image.isNull() && effect.index <= frame.index) {
_effect->markFrameShown();
}
}
void MediaPreviewWidget::resizeEvent(QResizeEvent *e) {
update();
}
QPoint MediaPreviewWidget::innerPosition(QSize size) const {
if (!_document || !_document->isPremiumSticker()) {
return QPoint(
(width() - size.width()) / 2,
(height() - size.height()) / 2);
}
const auto outer = size * kPremiumMultiplier;
const auto shift = outer.width() / 40;
return outerPosition(size)
+ QPoint(
outer.width() - size.width() - shift,
(outer.height() - size.height()) / 2);
}
QPoint MediaPreviewWidget::outerPosition(QSize size) const {
const auto outer = size * kPremiumMultiplier;
return QPoint(
(width() - outer.width()) / 2,
(height() - outer.height()) / 2);
}
void MediaPreviewWidget::showPreview(
Data::FileOrigin origin,
not_null<DocumentData*> document) {
@ -189,6 +229,7 @@ void MediaPreviewWidget::fillEmojiString() {
void MediaPreviewWidget::resetGifAndCache() {
_lottie = nullptr;
_effect = nullptr;
_gif.reset();
_gifThumbnail.reset();
_gifLastPosition = 0;
@ -220,6 +261,9 @@ QSize MediaPreviewWidget::currentDimensions() const {
}
if (_document->sticker()) {
box = QSize(st::maxStickerSize, st::maxStickerSize);
if (_document->isPremiumSticker()) {
result = (box /= kPremiumDownscale);
}
} else {
box = QSize(2 * st::maxStickerSize, 2 * st::maxStickerSize);
}
@ -239,22 +283,49 @@ QSize MediaPreviewWidget::currentDimensions() const {
return result;
}
void MediaPreviewWidget::createLottieIfReady(
not_null<DocumentData*> document) {
const auto sticker = document->sticker();
if (!sticker
|| !sticker->isLottie()
|| _lottie
|| !_documentMedia->loaded()) {
return;
} else if (document->isPremiumSticker()
&& _documentMedia->videoThumbnailContent().isEmpty()) {
return;
}
const_cast<MediaPreviewWidget*>(this)->setupLottie();
}
void MediaPreviewWidget::setupLottie() {
Expects(_document != nullptr);
const auto factor = cIntRetinaFactor();
const auto size = currentDimensions();
_lottie = std::make_unique<Lottie::SinglePlayer>(
Lottie::ReadContent(_documentMedia->bytes(), _document->filepath()),
Lottie::FrameRequest{ currentDimensions() * cIntRetinaFactor() },
Lottie::FrameRequest{ size * factor },
Lottie::Quality::High);
if (_document->isPremiumSticker()) {
_effect = std::make_unique<Lottie::SinglePlayer>(
Lottie::ReadContent(_documentMedia->videoThumbnailContent(), {}),
Lottie::FrameRequest{ size * kPremiumMultiplier * factor },
Lottie::Quality::High);
}
_lottie->updates(
) | rpl::start_with_next([=](Lottie::Update update) {
const auto handler = [=](Lottie::Update update) {
v::match(update.data, [&](const Lottie::Information &) {
this->update();
}, [&](const Lottie::DisplayFrameRequest &) {
this->update(updateArea());
});
}, lifetime());
};
_lottie->updates() | rpl::start_with_next(handler, lifetime());
if (_effect) {
_effect->updates() | rpl::start_with_next(handler, lifetime());
}
}
QPixmap MediaPreviewWidget::currentImage() const {
@ -264,9 +335,8 @@ QPixmap MediaPreviewWidget::currentImage() const {
const auto webm = sticker && sticker->isWebm();
if (sticker && !webm) {
if (_cacheStatus != CacheLoaded) {
if (sticker->isLottie() && !_lottie && _documentMedia->loaded()) {
const_cast<MediaPreviewWidget*>(this)->setupLottie();
}
const_cast<MediaPreviewWidget*>(this)->createLottieIfReady(
_document);
if (_lottie && _lottie->ready()) {
return QPixmap();
} else if (const auto image = _documentMedia->getStickerLarge()) {

View File

@ -50,10 +50,13 @@ private:
void startGifAnimation(const Media::Clip::ReaderPointer &gif);
QSize currentDimensions() const;
QPixmap currentImage() const;
void createLottieIfReady(not_null<DocumentData*> document);
void setupLottie();
void startShow();
void fillEmojiString();
void resetGifAndCache();
[[nodiscard]] QPoint innerPosition(QSize size) const;
[[nodiscard]] QPoint outerPosition(QSize size) const;
[[nodiscard]] QRect updateArea() const;
not_null<Window::SessionController*> _controller;
@ -69,6 +72,7 @@ private:
bool _gifWithAlpha = false;
crl::time _gifLastPosition = 0;
std::unique_ptr<Lottie::SinglePlayer> _lottie;
std::unique_ptr<Lottie::SinglePlayer> _effect;
int _emojiSize;
std::vector<not_null<EmojiPtr>> _emojiList;