diff --git a/Telegram/SourceFiles/storage/localimageloader.cpp b/Telegram/SourceFiles/storage/localimageloader.cpp index 7a369af71..49b8151c7 100644 --- a/Telegram/SourceFiles/storage/localimageloader.cpp +++ b/Telegram/SourceFiles/storage/localimageloader.cpp @@ -25,6 +25,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "history/history_item.h" #include "boxes/send_files_box.h" #include "ui/boxes/confirm_box.h" +#include "ui/image/image_prepare.h" #include "lang/lang_keys.h" #include "storage/file_download.h" #include "storage/storage_media_prepare.h" @@ -42,6 +43,7 @@ namespace { constexpr auto kThumbnailQuality = 87; constexpr auto kThumbnailSize = 320; constexpr auto kPhotoUploadPartSize = 32 * 1024; +constexpr auto kRecompressAfterBpp = 4; using Ui::ValidateThumbDimensions; @@ -53,7 +55,7 @@ struct PreparedFileThumbnail { MTPPhotoSize mtpSize = MTP_photoSizeEmpty(MTP_string()); }; -PreparedFileThumbnail PrepareFileThumbnail(QImage &&original) { +[[nodiscard]] PreparedFileThumbnail PrepareFileThumbnail(QImage &&original) { const auto width = original.width(); const auto height = original.height(); if (!ValidateThumbDimensions(width, height)) { @@ -87,7 +89,9 @@ PreparedFileThumbnail PrepareFileThumbnail(QImage &&original) { return result; } -bool FileThumbnailUploadRequired(const QString &filemime, int32 filesize) { +[[nodiscard]] bool FileThumbnailUploadRequired( + const QString &filemime, + int32 filesize) { constexpr auto kThumbnailUploadBySize = 5 * 1024 * 1024; const auto kThumbnailKnownMimes = { "image/jpeg", @@ -101,7 +105,7 @@ bool FileThumbnailUploadRequired(const QString &filemime, int32 filesize) { == end(kThumbnailKnownMimes)); } -PreparedFileThumbnail FinalizeFileThumbnail( +[[nodiscard]] PreparedFileThumbnail FinalizeFileThumbnail( PreparedFileThumbnail &&prepared, const QString &filemime, int32 filesize, @@ -115,7 +119,7 @@ PreparedFileThumbnail FinalizeFileThumbnail( return std::move(prepared); } -auto FindAlbumItem( +[[nodiscard]] auto FindAlbumItem( std::vector &items, not_null item) { const auto result = ranges::find( @@ -127,7 +131,7 @@ auto FindAlbumItem( return result; } -MTPInputSingleMedia PrepareAlbumItemMedia( +[[nodiscard]] MTPInputSingleMedia PrepareAlbumItemMedia( not_null item, const MTPInputMedia &media, uint64 randomId) { @@ -149,7 +153,7 @@ MTPInputSingleMedia PrepareAlbumItemMedia( sentEntities); } -std::vector> ExtractStickersFromScene( +[[nodiscard]] std::vector> ExtractStickersFromScene( not_null info) { const auto allItems = info->modifications.paint->items(); @@ -162,6 +166,33 @@ std::vector> ExtractStickersFromScene( }) | ranges::to_vector; } +[[nodiscard]] QByteArray ComputePhotoJpegBytes( + QImage &full, + const QByteArray &bytes, + const QByteArray &format) { + if (!bytes.isEmpty() + && (bytes.size() + <= full.width() * full.height() * kRecompressAfterBpp / 8) + && (format == u"jpeg"_q) + && Images::IsProgressiveJpeg(bytes)) { + return bytes; + } + + // We have an example of dark .png image that when being sent without + // removing its color space is displayed fine on tdesktop, but with + // a light gray background on mobile apps. + full.setColorSpace(QColorSpace()); + auto result = QByteArray(); + QBuffer buffer(&result); + QImageWriter writer(&buffer, "JPEG"); + writer.setQuality(87); + writer.setProgressiveScanWrite(true); + writer.write(full); + buffer.close(); + + return result; +} + } // namespace SendMediaPrepare::SendMediaPrepare( @@ -663,15 +694,23 @@ bool FileLoadTask::CheckForImage( return Images::Read({ .path = filepath, .content = content, + .returnContent = true, }); }(); - return FillImageInformation(std::move(read.image), read.animated, result); + return FillImageInformation( + std::move(read.image), + read.animated, + result, + std::move(read.content), + std::move(read.format)); } bool FileLoadTask::FillImageInformation( QImage &&image, bool animated, - std::unique_ptr &result) { + std::unique_ptr &result, + QByteArray content, + QByteArray format) { Expects(result != nullptr); if (image.isNull()) { @@ -679,6 +718,8 @@ bool FileLoadTask::FillImageInformation( } auto media = Ui::PreparedFileInformation::Image(); media.data = std::move(image); + media.bytes = std::move(content); + media.format = std::move(format); media.animated = animated; result->media = media; return true; @@ -703,6 +744,8 @@ void FileLoadTask::process(Args &&args) { auto isSticker = false; auto fullimage = QImage(); + auto fullimagebytes = QByteArray(); + auto fullimageformat = QByteArray(); auto info = _filepath.isEmpty() ? QFileInfo() : QFileInfo(_filepath); if (info.exists()) { if (info.isDir()) { @@ -724,8 +767,12 @@ void FileLoadTask::process(Args &&args) { if (auto image = std::get_if( &_information->media)) { fullimage = base::take(image->data); - if (!Core::IsMimeSticker(filemime)) { + fullimagebytes = base::take(image->bytes); + fullimageformat = base::take(image->format); + if (!Core::IsMimeSticker(filemime) + && fullimageformat != u"jpeg"_q) { fullimage = Images::Opaque(std::move(fullimage)); + fullimagebytes = fullimageformat = QByteArray(); } isAnimation = image->animated; } @@ -739,12 +786,16 @@ void FileLoadTask::process(Args &&args) { if (auto image = std::get_if( &_information->media)) { fullimage = base::take(image->data); + fullimagebytes = base::take(image->bytes); + fullimageformat = base::take(image->format); } } const auto mimeType = Core::MimeTypeForData(_content); filemime = mimeType.name(); - if (!Core::IsMimeSticker(filemime)) { + if (!Core::IsMimeSticker(filemime) + && fullimageformat != u"jpeg"_q) { fullimage = Images::Opaque(std::move(fullimage)); + fullimagebytes = fullimageformat = QByteArray(); } if (filemime == "image/jpeg") { filename = filedialogDefaultName(qsl("photo"), qsl(".jpg"), QString(), true); @@ -764,6 +815,8 @@ void FileLoadTask::process(Args &&args) { if (auto image = std::get_if( &_information->media)) { fullimage = base::take(image->data); + fullimagebytes = base::take(image->bytes); + fullimageformat = base::take(image->format); } } if (!fullimage.isNull() && fullimage.width() > 0) { @@ -786,6 +839,7 @@ void FileLoadTask::process(Args &&args) { filesize = _content.size(); } fullimage = Images::Opaque(std::move(fullimage)); + fullimagebytes = fullimageformat = QByteArray(); } } _result->filesize = (int32)qMin(filesize, qint64(INT_MAX)); @@ -878,18 +932,14 @@ void FileLoadTask::process(Args &&args) { } else if (filemime.startsWith(u"image/"_q) && _type != SendMediaType::File) { auto medium = (w > 320 || h > 320) ? fullimage.scaled(320, 320, Qt::KeepAspectRatio, Qt::SmoothTransformation) : fullimage; - auto full = (w > 1280 || h > 1280) ? fullimage.scaled(1280, 1280, Qt::KeepAspectRatio, Qt::SmoothTransformation) : fullimage; - { - // We have an example of dark .png image that when being sent without - // removing its color space is displayed fine on tdesktop, but with - // a light gray background on mobile apps. - full.setColorSpace(QColorSpace()); - QBuffer buffer(&filedata); - QImageWriter writer(&buffer, "JPEG"); - writer.setQuality(87); - writer.setProgressiveScanWrite(true); - writer.write(full); + + const auto downscaled = (w > 1280 || h > 1280); + auto full = downscaled ? fullimage.scaled(1280, 1280, Qt::KeepAspectRatio, Qt::SmoothTransformation) : fullimage; + if (downscaled) { + fullimagebytes = fullimageformat = QByteArray(); } + filedata = ComputePhotoJpegBytes(full, fullimagebytes, fullimageformat); + photoThumbs.emplace('m', PreparedPhotoThumb{ .image = medium }); photoSizes.push_back(MTP_photoSize(MTP_string("m"), MTP_int(medium.width()), MTP_int(medium.height()), MTP_int(0))); diff --git a/Telegram/SourceFiles/storage/localimageloader.h b/Telegram/SourceFiles/storage/localimageloader.h index 6bb5ba88e..7442bd3dd 100644 --- a/Telegram/SourceFiles/storage/localimageloader.h +++ b/Telegram/SourceFiles/storage/localimageloader.h @@ -263,7 +263,9 @@ public: static bool FillImageInformation( QImage &&image, bool animated, - std::unique_ptr &result); + std::unique_ptr &result, + QByteArray content = {}, + QByteArray format = {}); FileLoadTask( not_null session, diff --git a/Telegram/SourceFiles/ui/chat/attach/attach_prepare.h b/Telegram/SourceFiles/ui/chat/attach/attach_prepare.h index a48a5b031..b1f1e9332 100644 --- a/Telegram/SourceFiles/ui/chat/attach/attach_prepare.h +++ b/Telegram/SourceFiles/ui/chat/attach/attach_prepare.h @@ -20,6 +20,8 @@ class SendFilesWay; struct PreparedFileInformation { struct Image { QImage data; + QByteArray bytes; + QByteArray format; bool animated = false; Editor::PhotoModifications modifications; }; diff --git a/Telegram/lib_ui b/Telegram/lib_ui index dd3cc5000..81e216f1c 160000 --- a/Telegram/lib_ui +++ b/Telegram/lib_ui @@ -1 +1 @@ -Subproject commit dd3cc5000e4f136ee198c5ace014640593e0aa2c +Subproject commit 81e216f1cede22d30573233c36039716cc438b33