From 7034df49e91345955d1973efddfa8e01f24234ef Mon Sep 17 00:00:00 2001 From: John Preston Date: Wed, 3 Jul 2019 13:03:01 +0200 Subject: [PATCH] Fix sending of .tgs stickers. --- Telegram/SourceFiles/boxes/send_files_box.cpp | 33 ++++++++-- Telegram/SourceFiles/core/mime_type.cpp | 7 ++ Telegram/SourceFiles/core/mime_type.h | 1 + .../SourceFiles/storage/localimageloader.cpp | 65 +++++++++---------- .../storage/storage_media_prepare.cpp | 5 +- 5 files changed, 72 insertions(+), 39 deletions(-) diff --git a/Telegram/SourceFiles/boxes/send_files_box.cpp b/Telegram/SourceFiles/boxes/send_files_box.cpp index b0ea3da09..9608caf6d 100644 --- a/Telegram/SourceFiles/boxes/send_files_box.cpp +++ b/Telegram/SourceFiles/boxes/send_files_box.cpp @@ -27,6 +27,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "ui/grouped_layout.h" #include "ui/text_options.h" #include "ui/special_buttons.h" +#include "lottie/lottie_single_player.h" #include "data/data_document.h" #include "media/clip/media_clip_reader.h" #include "window/window_session_controller.h" @@ -41,6 +42,7 @@ constexpr auto kMinPreviewWidth = 20; constexpr auto kShrinkDuration = crl::time(150); constexpr auto kDragDuration = crl::time(200); const auto kStickerMimeString = qstr("image/webp"); +const auto kAnimatedStickerMimeString = qstr("application/x-tgsticker"); class SingleMediaPreview : public Ui::RpWidget { public: @@ -82,6 +84,7 @@ private: int _previewWidth = 0; int _previewHeight = 0; Media::Clip::ReaderPointer _gifPreview; + std::unique_ptr _lottiePreview; }; @@ -590,7 +593,8 @@ SingleMediaPreview *SingleMediaPreview::Create( preview.height())) { return nullptr; } - const auto sticker = (file.information->filemime == kStickerMimeString); + const auto sticker = (file.information->filemime == kStickerMimeString) + || (file.information->filemime == kAnimatedStickerMimeString); return Ui::CreateChild( parent, controller, @@ -627,7 +631,7 @@ void SingleMediaPreview::preparePreview( const QString &animatedPreviewPath) { auto maxW = 0; auto maxH = 0; - if (_animated) { + if (_animated && !_sticker) { auto limitW = st::sendMediaPreviewSize; auto limitH = st::confirmMaxHeight; maxW = qMax(preview.width(), 1); @@ -683,7 +687,17 @@ void SingleMediaPreview::preparePreview( void SingleMediaPreview::prepareAnimatedPreview( const QString &animatedPreviewPath) { - if (!animatedPreviewPath.isEmpty()) { + if (_sticker && _animated) { + const auto box = QSize(_previewWidth, _previewHeight) + * cIntRetinaFactor(); + _lottiePreview = std::make_unique( + Lottie::ReadContent(QByteArray(), animatedPreviewPath), + Lottie::FrameRequest{ box }); + _lottiePreview->updates( + ) | rpl::start_with_next([=] { + update(); + }, lifetime()); + } else if (!animatedPreviewPath.isEmpty()) { auto callback = [=](Media::Clip::Notification notification) { clipCallback(notification); }; @@ -734,10 +748,21 @@ void SingleMediaPreview::paintEvent(QPaintEvent *e) { auto paused = _controller->isGifPausedAtLeastFor(Window::GifPauseReason::Layer); auto frame = _gifPreview->current(s.width(), s.height(), s.width(), s.height(), ImageRoundRadius::None, RectPart::None, paused ? 0 : crl::now()); p.drawPixmap(_previewLeft, st::boxPhotoPadding.top(), frame); + } else if (_lottiePreview && _lottiePreview->ready()) { + const auto frame = _lottiePreview->frame(); + const auto size = frame.size() / cIntRetinaFactor(); + p.drawImage( + QRect( + _previewLeft + (_previewWidth - size.width()) / 2, + st::boxPhotoPadding.top() + (_previewHeight - size.height()) / 2, + size.width(), + size.height()), + frame); + _lottiePreview->markFrameShown(); } else { p.drawPixmap(_previewLeft, st::boxPhotoPadding.top(), _preview); } - if (_animated && !_gifPreview) { + if (_animated && !_gifPreview && !_lottiePreview) { auto inner = QRect(_previewLeft + (_previewWidth - st::msgFileSize) / 2, st::boxPhotoPadding.top() + (_previewHeight - st::msgFileSize) / 2, st::msgFileSize, st::msgFileSize); p.setPen(Qt::NoPen); p.setBrush(st::msgDateImgBg); diff --git a/Telegram/SourceFiles/core/mime_type.cpp b/Telegram/SourceFiles/core/mime_type.cpp index 16b0059c7..83c078038 100644 --- a/Telegram/SourceFiles/core/mime_type.cpp +++ b/Telegram/SourceFiles/core/mime_type.cpp @@ -18,6 +18,7 @@ MimeType::MimeType(Known type) : _type(type) { QStringList MimeType::globPatterns() const { switch (_type) { case Known::WebP: return QStringList(qsl("*.webp")); + case Known::Tgs: return QStringList(qsl("*.tgs")); case Known::TDesktopTheme: return QStringList(qsl("*.tdesktop-theme")); case Known::TDesktopPalette: return QStringList(qsl("*.tdesktop-palette")); default: break; @@ -28,6 +29,7 @@ QStringList MimeType::globPatterns() const { QString MimeType::filterString() const { switch (_type) { case Known::WebP: return qsl("WebP image (*.webp)"); + case Known::Tgs: return qsl("Telegram sticker (*.tgs)"); case Known::TDesktopTheme: return qsl("Theme files (*.tdesktop-theme)"); case Known::TDesktopPalette: return qsl("Palette files (*.tdesktop-palette)"); default: break; @@ -38,6 +40,7 @@ QString MimeType::filterString() const { QString MimeType::name() const { switch (_type) { case Known::WebP: return qsl("image/webp"); + case Known::Tgs: return qsl("application/x-tgsticker"); case Known::TDesktopTheme: return qsl("application/x-tdesktop-theme"); case Known::TDesktopPalette: return qsl("application/x-tdesktop-palette"); default: break; @@ -48,6 +51,8 @@ QString MimeType::name() const { MimeType MimeTypeForName(const QString &mime) { if (mime == qstr("image/webp")) { return MimeType(MimeType::Known::WebP); + } else if (mime == qstr("application/x-tgsticker")) { + return MimeType(MimeType::Known::Tgs); } else if (mime == qstr("application/x-tdesktop-theme")) { return MimeType(MimeType::Known::TDesktopTheme); } else if (mime == qstr("application/x-tdesktop-palette")) { @@ -62,6 +67,8 @@ MimeType MimeTypeForFile(const QFileInfo &file) { QString path = file.absoluteFilePath(); if (path.endsWith(qstr(".webp"), Qt::CaseInsensitive)) { return MimeType(MimeType::Known::WebP); + } else if (path.endsWith(qstr(".tgs"), Qt::CaseInsensitive)) { + return MimeType(MimeType::Known::Tgs); } else if (path.endsWith(qstr(".tdesktop-theme"), Qt::CaseInsensitive)) { return MimeType(MimeType::Known::TDesktopTheme); } else if (path.endsWith(qstr(".tdesktop-palette"), Qt::CaseInsensitive)) { diff --git a/Telegram/SourceFiles/core/mime_type.h b/Telegram/SourceFiles/core/mime_type.h index dec9081cc..dd7ec2f05 100644 --- a/Telegram/SourceFiles/core/mime_type.h +++ b/Telegram/SourceFiles/core/mime_type.h @@ -20,6 +20,7 @@ public: TDesktopTheme, TDesktopPalette, WebP, + Tgs, }; explicit MimeType(const QMimeType &type); diff --git a/Telegram/SourceFiles/storage/localimageloader.cpp b/Telegram/SourceFiles/storage/localimageloader.cpp index a90f30f08..539f48ab9 100644 --- a/Telegram/SourceFiles/storage/localimageloader.cpp +++ b/Telegram/SourceFiles/storage/localimageloader.cpp @@ -78,13 +78,8 @@ PreparedFileThumbnail PrepareFileThumbnail(QImage &&original) { PreparedFileThumbnail PrepareAnimatedStickerThumbnail( const QString &file, const QByteArray &bytes) { - return PrepareFileThumbnail(Lottie::ReadThumbnail([&] { - if (!bytes.isEmpty()) { - return bytes; - } - auto f = QFile(file); - return f.open(QIODevice::ReadOnly) ? f.readAll() : QByteArray(); - }())); + return PrepareFileThumbnail( + Lottie::ReadThumbnail(Lottie::ReadContent(bytes, file))); } bool FileThumbnailUploadRequired(const QString &filemime, int32 filesize) { @@ -683,14 +678,23 @@ bool FileLoadTask::CheckForImage( const QByteArray &content, std::unique_ptr &result) { auto animated = false; - auto image = ([&filepath, &content, &animated] { + auto image = [&] { + if (filepath.endsWith(qstr(".tgs"), Qt::CaseInsensitive)) { + auto image = Lottie::ReadThumbnail( + Lottie::ReadContent(content, filepath)); + if (!image.isNull()) { + animated = true; + result->filemime = qstr("application/x-tgsticker"); + } + return image; + } if (!content.isEmpty()) { return App::readImage(content, nullptr, false, &animated); } else if (!filepath.isEmpty()) { return App::readImage(filepath, nullptr, false, &animated); } return QImage(); - })(); + }(); return FillImageInformation(std::move(image), animated, result); } @@ -712,6 +716,7 @@ bool FileLoadTask::FillImageInformation( void FileLoadTask::process() { const auto stickerMime = qsl("image/webp"); + const auto animatedStickerMime = qsl("application/x-tgsticker"); _result = std::make_shared( id(), @@ -754,7 +759,7 @@ void FileLoadTask::process() { if (auto image = base::get_if( &_information->media)) { fullimage = base::take(image->data); - if (auto opaque = (filemime != stickerMime)) { + if (filemime != stickerMime && filemime != animatedStickerMime) { fullimage = Images::prepareOpaque(std::move(fullimage)); } isAnimation = image->animated; @@ -773,7 +778,7 @@ void FileLoadTask::process() { } const auto mimeType = Core::MimeTypeForData(_content); filemime = mimeType.name(); - if (filemime != stickerMime) { + if (filemime != stickerMime && filemime != animatedStickerMime) { fullimage = Images::prepareOpaque(std::move(fullimage)); } if (filemime == "image/jpeg") { @@ -830,10 +835,6 @@ void FileLoadTask::process() { QByteArray goodThumbnailBytes; QVector attributes(1, MTP_documentAttributeFilename(MTP_string(filename))); - const auto checkAnimatedSticker = filename.endsWith(qstr(".tgs"), Qt::CaseInsensitive); - if (checkAnimatedSticker) { - filemime = "application/x-tgsticker"; - } auto thumbnail = PreparedFileThumbnail(); @@ -872,8 +873,6 @@ void FileLoadTask::process() { } thumbnail = PrepareFileThumbnail(std::move(video->thumbnail)); - } else if (checkAnimatedSticker) { - thumbnail = PrepareAnimatedStickerThumbnail(_filepath, _content); } } @@ -882,7 +881,20 @@ void FileLoadTask::process() { attributes.push_back(MTP_documentAttributeImageSize(MTP_int(w), MTP_int(h))); if (ValidateThumbDimensions(w, h)) { - if (isAnimation) { + isSticker = (filemime == stickerMime + || filemime == animatedStickerMime) + && (w > 0) + && (h > 0) + && (w <= StickerMaxSize) + && (h <= StickerMaxSize) + && (filesize < Storage::kMaxStickerInMemory); + if (isSticker) { + attributes.push_back(MTP_documentAttributeSticker( + MTP_flags(0), + MTP_string(QString()), + MTP_inputStickerSetEmpty(), + MTPMaskCoords())); + } else if (isAnimation) { attributes.push_back(MTP_documentAttributeAnimated()); } else if (_type != SendMediaType::File) { auto thumb = (w > 100 || h > 100) ? fullimage.scaled(100, 100, Qt::KeepAspectRatio, Qt::SmoothTransformation) : fullimage; @@ -915,21 +927,6 @@ void FileLoadTask::process() { filesize = _result->filesize = filedata.size(); } } - - isSticker = !isAnimation - && (filemime == stickerMime) - && (w > 0) - && (h > 0) - && (w <= StickerMaxSize) - && (h <= StickerMaxSize) - && (filesize < Storage::kMaxStickerInMemory); - if (isSticker) { - attributes.push_back(MTP_documentAttributeSticker( - MTP_flags(0), - MTP_string(QString()), - MTP_inputStickerSetEmpty(), - MTPMaskCoords())); - } thumbnail = PrepareFileThumbnail(std::move(fullimage)); } } @@ -937,7 +934,7 @@ void FileLoadTask::process() { std::move(thumbnail), filemime, filesize, - isSticker || checkAnimatedSticker); + isSticker); if (_type == SendMediaType::Photo && photo.type() == mtpc_photoEmpty) { _type = SendMediaType::File; diff --git a/Telegram/SourceFiles/storage/storage_media_prepare.cpp b/Telegram/SourceFiles/storage/storage_media_prepare.cpp index a88f6fae1..1276e14cf 100644 --- a/Telegram/SourceFiles/storage/storage_media_prepare.cpp +++ b/Telegram/SourceFiles/storage/storage_media_prepare.cpp @@ -316,7 +316,10 @@ bool PreparedList::canAddCaption(bool isAlbum, bool compressImages) const { if (files.empty() || compressImages) { return false; } - return (files.front().mime == qstr("image/webp")); + return (files.front().mime == qstr("image/webp")) + || files.front().path.endsWith( + qstr(".tgs"), + Qt::CaseInsensitive); }; return isAlbum || (files.size() == 1 && !isSticker()); }