247 lines
6.2 KiB
C++
247 lines
6.2 KiB
C++
/*
|
|
This file is part of Telegram Desktop,
|
|
the official desktop application for the Telegram messaging service.
|
|
|
|
For license and copyright information please follow this link:
|
|
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|
*/
|
|
#include "info/userpic/info_userpic_emoji_builder_preview.h"
|
|
|
|
#include "chat_helpers/stickers_lottie.h"
|
|
#include "data/data_document.h"
|
|
#include "data/data_document_media.h"
|
|
#include "data/data_session.h"
|
|
#include "history/view/media/history_view_sticker_player.h"
|
|
#include "info/userpic/info_userpic_emoji_builder_common.h"
|
|
#include "main/main_app_config.h"
|
|
#include "main/main_session.h"
|
|
#include "ui/painter.h"
|
|
#include "ui/rect.h"
|
|
|
|
namespace UserpicBuilder {
|
|
|
|
PreviewPainter::PreviewPainter(int size)
|
|
: _size(size)
|
|
, _emojiSize(base::SafeRound(_size / M_SQRT2))
|
|
, _frameGeometry(Rect(Size(_size)) - Margins((_size - _emojiSize) / 2))
|
|
, _frameRect(Rect(_frameGeometry.size()))
|
|
, _mask(
|
|
_frameRect.size() * style::DevicePixelRatio(),
|
|
QImage::Format_ARGB32_Premultiplied)
|
|
, _frame(_mask.size(), QImage::Format_ARGB32_Premultiplied) {
|
|
_frame.setDevicePixelRatio(style::DevicePixelRatio());
|
|
_mask.setDevicePixelRatio(style::DevicePixelRatio());
|
|
_mask.fill(Qt::transparent);
|
|
{
|
|
auto p = QPainter(&_mask);
|
|
auto hq = PainterHighQualityEnabler(p);
|
|
p.setPen(Qt::NoPen);
|
|
p.setBrush(st::windowBg);
|
|
constexpr auto kFrameRadiusPercent = 25;
|
|
p.drawRoundedRect(
|
|
_frameRect,
|
|
kFrameRadiusPercent,
|
|
kFrameRadiusPercent,
|
|
Qt::RelativeSize);
|
|
}
|
|
}
|
|
|
|
DocumentData *PreviewPainter::document() const {
|
|
return _media ? _media->owner().get() : nullptr;
|
|
}
|
|
|
|
void PreviewPainter::setPlayOnce(bool value) {
|
|
_playOnce = value;
|
|
}
|
|
|
|
void PreviewPainter::setDocument(
|
|
not_null<DocumentData*> document,
|
|
Fn<void()> updateCallback) {
|
|
if (_media && (document == _media->owner())) {
|
|
return;
|
|
}
|
|
_lifetime.destroy();
|
|
|
|
const auto sticker = document->sticker();
|
|
Assert(sticker != nullptr);
|
|
_media = document->createMediaView();
|
|
_media->checkStickerLarge();
|
|
_media->goodThumbnailWanted();
|
|
|
|
if (_playOnce) {
|
|
_firstFrameShown = false;
|
|
_paused = false;
|
|
} else {
|
|
_paused = true;
|
|
}
|
|
|
|
rpl::single() | rpl::then(
|
|
document->owner().session().downloaderTaskFinished()
|
|
) | rpl::start_with_next([=] {
|
|
if (!_media->loaded()) {
|
|
return;
|
|
}
|
|
_lifetime.destroy();
|
|
const auto emojiSize = Size(_size * style::DevicePixelRatio());
|
|
if (sticker->isLottie()) {
|
|
_player = std::make_unique<HistoryView::LottiePlayer>(
|
|
ChatHelpers::LottiePlayerFromDocument(
|
|
_media.get(),
|
|
//
|
|
ChatHelpers::StickerLottieSize::EmojiInteractionReserved7,
|
|
emojiSize,
|
|
Lottie::Quality::High));
|
|
} else if (sticker->isWebm()) {
|
|
_player = std::make_unique<HistoryView::WebmPlayer>(
|
|
_media->owner()->location(),
|
|
_media->bytes(),
|
|
emojiSize);
|
|
} else if (sticker) {
|
|
_player = std::make_unique<HistoryView::StaticStickerPlayer>(
|
|
_media->owner()->location(),
|
|
_media->bytes(),
|
|
emojiSize);
|
|
}
|
|
if (_player) {
|
|
_player->setRepaintCallback(updateCallback);
|
|
} else {
|
|
updateCallback();
|
|
}
|
|
}, _lifetime);
|
|
}
|
|
|
|
void PreviewPainter::paintBackground(QPainter &p, const QImage &image) {
|
|
p.drawImage(0, 0, image);
|
|
}
|
|
|
|
bool PreviewPainter::paintForeground(QPainter &p) {
|
|
if (_player && _player->ready()) {
|
|
const auto c = _media->owner()->emojiUsesTextColor() ? 255 : 0;
|
|
auto frame = _player->frame(
|
|
Size(_emojiSize),
|
|
QColor(c, c, c, c),
|
|
false,
|
|
crl::now(),
|
|
_paused);
|
|
|
|
if (_playOnce) {
|
|
if (!_firstFrameShown && (frame.index == 1)) {
|
|
_firstFrameShown = true;
|
|
} else if (_firstFrameShown && !frame.index) {
|
|
_paused = true;
|
|
}
|
|
}
|
|
|
|
_frame.fill(Qt::transparent);
|
|
{
|
|
QPainter q(&_frame);
|
|
if (frame.image.width() == frame.image.height()) {
|
|
q.drawImage(_frameRect, frame.image);
|
|
} else {
|
|
auto frameRect = Rect(frame.image.size().scaled(
|
|
_frameRect.size(),
|
|
Qt::KeepAspectRatio));
|
|
frameRect.moveCenter(_frameRect.center());
|
|
q.drawImage(frameRect, frame.image);
|
|
}
|
|
q.setCompositionMode(QPainter::CompositionMode_DestinationIn);
|
|
q.drawImage(0, 0, _mask);
|
|
}
|
|
|
|
p.drawImage(_frameGeometry.topLeft(), _frame);
|
|
if (!_paused) {
|
|
_player->markFrameShown();
|
|
}
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
EmojiUserpic::EmojiUserpic(
|
|
not_null<Ui::RpWidget*> parent,
|
|
const QSize &size,
|
|
bool isForum)
|
|
: Ui::RpWidget(parent)
|
|
, _forum(isForum)
|
|
, _painter(size.width())
|
|
, _duration(st::slideWrapDuration) {
|
|
resize(size);
|
|
}
|
|
|
|
void EmojiUserpic::setDocument(not_null<DocumentData*> document) {
|
|
if (!_playOnce.has_value()) {
|
|
const auto &c = document->owner().session().appConfig();
|
|
_playOnce = !c.get<bool>(u"upload_markup_video"_q, false);
|
|
}
|
|
_painter.setDocument(document, [=] { update(); });
|
|
_painter.setPlayOnce(*_playOnce);
|
|
}
|
|
|
|
void EmojiUserpic::result(int size, Fn<void(UserpicBuilder::Result)> done) {
|
|
const auto painter = lifetime().make_state<PreviewPainter>(size);
|
|
// Reset to the first frame.
|
|
const auto document = _painter.document();
|
|
const auto callback = [=] {
|
|
auto background = GenerateGradient(Size(size), _colors, false);
|
|
|
|
{
|
|
auto p = QPainter(&background);
|
|
while (true) {
|
|
if (painter->paintForeground(p)) {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
if (*_playOnce && document) {
|
|
done({ std::move(background), document->id, _colors });
|
|
} else {
|
|
done({ std::move(background) });
|
|
}
|
|
};
|
|
if (document) {
|
|
painter->setDocument(document, callback);
|
|
} else {
|
|
callback();
|
|
}
|
|
}
|
|
|
|
void EmojiUserpic::setGradientColors(std::vector<QColor> colors) {
|
|
if (_colors == colors) {
|
|
return;
|
|
}
|
|
if (const auto colors = base::take(_colors); !colors.empty()) {
|
|
_previousImage = GenerateGradient(size(), colors, !_forum, _forum);
|
|
}
|
|
_colors = std::move(colors);
|
|
{
|
|
_image = GenerateGradient(size(), _colors, !_forum, _forum);
|
|
}
|
|
if (_duration) {
|
|
_animation.stop();
|
|
_animation.start([=] { update(); }, 0., 1., _duration);
|
|
} else {
|
|
update();
|
|
}
|
|
}
|
|
|
|
void EmojiUserpic::paintEvent(QPaintEvent *event) {
|
|
auto p = QPainter(this);
|
|
|
|
if (_animation.animating() && !_previousImage.isNull()) {
|
|
_painter.paintBackground(p, _previousImage);
|
|
|
|
p.setOpacity(_animation.value(1.));
|
|
}
|
|
|
|
_painter.paintBackground(p, _image);
|
|
|
|
p.setOpacity(1.);
|
|
_painter.paintForeground(p);
|
|
}
|
|
|
|
void EmojiUserpic::setDuration(crl::time duration) {
|
|
_duration = duration;
|
|
}
|
|
|
|
} // namespace UserpicBuilder
|