mirror of
https://github.com/AyuGram/AyuGramDesktop.git
synced 2025-04-18 23:27:09 +02:00
Don't recompress some JPEGs when sending as photos.
If JPEG is saved in progressive mode and has bpp <= 4 and max(width, height) <= 1280 then we send original bytes.
This commit is contained in:
parent
e84ebc2a5c
commit
6805259f74
4 changed files with 77 additions and 23 deletions
|
@ -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<SendingAlbum::Item> &items,
|
||||
not_null<HistoryItem*> item) {
|
||||
const auto result = ranges::find(
|
||||
|
@ -127,7 +131,7 @@ auto FindAlbumItem(
|
|||
return result;
|
||||
}
|
||||
|
||||
MTPInputSingleMedia PrepareAlbumItemMedia(
|
||||
[[nodiscard]] MTPInputSingleMedia PrepareAlbumItemMedia(
|
||||
not_null<HistoryItem*> item,
|
||||
const MTPInputMedia &media,
|
||||
uint64 randomId) {
|
||||
|
@ -149,7 +153,7 @@ MTPInputSingleMedia PrepareAlbumItemMedia(
|
|||
sentEntities);
|
||||
}
|
||||
|
||||
std::vector<not_null<DocumentData*>> ExtractStickersFromScene(
|
||||
[[nodiscard]] std::vector<not_null<DocumentData*>> ExtractStickersFromScene(
|
||||
not_null<const Ui::PreparedFileInformation::Image*> info) {
|
||||
const auto allItems = info->modifications.paint->items();
|
||||
|
||||
|
@ -162,6 +166,33 @@ std::vector<not_null<DocumentData*>> 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<Ui::PreparedFileInformation> &result) {
|
||||
std::unique_ptr<Ui::PreparedFileInformation> &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<Ui::PreparedFileInformation::Image>(
|
||||
&_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<Ui::PreparedFileInformation::Image>(
|
||||
&_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<Ui::PreparedFileInformation::Image>(
|
||||
&_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)));
|
||||
|
||||
|
|
|
@ -263,7 +263,9 @@ public:
|
|||
static bool FillImageInformation(
|
||||
QImage &&image,
|
||||
bool animated,
|
||||
std::unique_ptr<Ui::PreparedFileInformation> &result);
|
||||
std::unique_ptr<Ui::PreparedFileInformation> &result,
|
||||
QByteArray content = {},
|
||||
QByteArray format = {});
|
||||
|
||||
FileLoadTask(
|
||||
not_null<Main::Session*> session,
|
||||
|
|
|
@ -20,6 +20,8 @@ class SendFilesWay;
|
|||
struct PreparedFileInformation {
|
||||
struct Image {
|
||||
QImage data;
|
||||
QByteArray bytes;
|
||||
QByteArray format;
|
||||
bool animated = false;
|
||||
Editor::PhotoModifications modifications;
|
||||
};
|
||||
|
|
|
@ -1 +1 @@
|
|||
Subproject commit dd3cc5000e4f136ee198c5ace014640593e0aa2c
|
||||
Subproject commit 81e216f1cede22d30573233c36039716cc438b33
|
Loading…
Add table
Reference in a new issue