From 771876429407e65305c1a758bf61e3a15d47c192 Mon Sep 17 00:00:00 2001 From: John Preston Date: Mon, 21 Feb 2022 17:23:49 +0300 Subject: [PATCH] Save original bytes of a photo on "Save to disk". --- Telegram/SourceFiles/data/data_cloud_file.cpp | 14 ++++---- Telegram/SourceFiles/data/data_cloud_file.h | 4 +-- Telegram/SourceFiles/data/data_document.cpp | 4 +-- Telegram/SourceFiles/data/data_photo.cpp | 10 +++--- .../SourceFiles/data/data_photo_media.cpp | 26 ++++++++++++-- Telegram/SourceFiles/data/data_photo_media.h | 10 +++++- .../media/view/media_view_overlay_widget.cpp | 36 +++++++++++++------ .../media/view/media_view_overlay_widget.h | 5 +++ 8 files changed, 80 insertions(+), 29 deletions(-) diff --git a/Telegram/SourceFiles/data/data_cloud_file.cpp b/Telegram/SourceFiles/data/data_cloud_file.cpp index 77ae77634..d60cce7ba 100644 --- a/Telegram/SourceFiles/data/data_cloud_file.cpp +++ b/Telegram/SourceFiles/data/data_cloud_file.cpp @@ -64,7 +64,7 @@ void CloudImage::set( session->data().cache(), kImageCacheTag, [=](FileOrigin origin) { load(session, origin); }, - [=](QImage preloaded) { + [=](QImage preloaded, QByteArray) { if (const auto view = activeView()) { view->set(session, data.preloaded); } @@ -80,7 +80,7 @@ void CloudImage::update( session->data().cache(), kImageCacheTag, [=](FileOrigin origin) { load(session, origin); }, - [=](QImage preloaded) { + [=](QImage preloaded, QByteArray) { if (const auto view = activeView()) { view->set(session, data.preloaded); } @@ -113,7 +113,7 @@ void CloudImage::load(not_null session, FileOrigin origin) { } return !(_file.flags & CloudFile::Flag::Loaded); }; - const auto done = [=](QImage result) { + const auto done = [=](QImage result, QByteArray) { if (const auto active = activeView()) { active->set(session, std::move(result)); } @@ -164,7 +164,7 @@ void UpdateCloudFile( Storage::Cache::Database &cache, uint8 cacheTag, Fn restartLoader, - Fn usePreloaded) { + Fn usePreloaded) { if (!data.location.valid()) { if (data.progressivePartSize && !file.location.valid()) { file.progressivePartSize = data.progressivePartSize; @@ -213,7 +213,7 @@ void UpdateCloudFile( if (!data.preloaded.isNull()) { file.loader = nullptr; if (usePreloaded) { - usePreloaded(data.preloaded); + usePreloaded(data.preloaded, data.bytes); } } else if (file.loader) { const auto origin = base::take(file.loader)->fileOrigin(); @@ -307,7 +307,7 @@ void LoadCloudFile( bool autoLoading, uint8 cacheTag, Fn finalCheck, - Fn done, + Fn done, Fn fail, Fn progress, int downloadFrontPartSize) { @@ -318,7 +318,7 @@ void LoadCloudFile( onstack(true); } } else if (const auto onstack = done) { - onstack(std::move(read)); + onstack(std::move(read), file.loader->bytes()); } }; LoadCloudFile( diff --git a/Telegram/SourceFiles/data/data_cloud_file.h b/Telegram/SourceFiles/data/data_cloud_file.h index 56a5e250b..fe1047d2a 100644 --- a/Telegram/SourceFiles/data/data_cloud_file.h +++ b/Telegram/SourceFiles/data/data_cloud_file.h @@ -96,7 +96,7 @@ void UpdateCloudFile( Storage::Cache::Database &cache, uint8 cacheTag, Fn restartLoader, - Fn usePreloaded = nullptr); + Fn usePreloaded = nullptr); void LoadCloudFile( not_null session, @@ -106,7 +106,7 @@ void LoadCloudFile( bool autoLoading, uint8 cacheTag, Fn finalCheck, - Fn done, + Fn done, Fn fail = nullptr, Fn progress = nullptr, int downloadFrontPartSize = 0); diff --git a/Telegram/SourceFiles/data/data_document.cpp b/Telegram/SourceFiles/data/data_document.cpp index 99974e028..efb0f0a8f 100644 --- a/Telegram/SourceFiles/data/data_document.cpp +++ b/Telegram/SourceFiles/data/data_document.cpp @@ -480,7 +480,7 @@ void DocumentData::updateThumbnails( owner().cache(), Data::kImageCacheTag, [&](Data::FileOrigin origin) { loadThumbnail(origin); }, - [&](QImage preloaded) { + [&](QImage preloaded, QByteArray) { if (const auto media = activeMediaView()) { media->setThumbnail(std::move(preloaded)); } @@ -530,7 +530,7 @@ void DocumentData::loadThumbnail(Data::FileOrigin origin) { } return true; }; - const auto done = [=](QImage result) { + const auto done = [=](QImage result, QByteArray) { if (const auto active = activeMediaView()) { active->setThumbnail(std::move(result)); } diff --git a/Telegram/SourceFiles/data/data_photo.cpp b/Telegram/SourceFiles/data/data_photo.cpp index 5ff525799..7eec4041a 100644 --- a/Telegram/SourceFiles/data/data_photo.cpp +++ b/Telegram/SourceFiles/data/data_photo.cpp @@ -296,7 +296,7 @@ void PhotoData::load( } return true; }; - const auto done = [=](QImage result) { + const auto done = [=](QImage result, QByteArray bytes) { Expects(_images[valid].loader != nullptr); // Find out what progressive photo size have we loaded exactly. @@ -316,7 +316,8 @@ void PhotoData::load( active->set( validSize, goodFor, - ValidatePhotoImage(std::move(result), _images[valid])); + ValidatePhotoImage(std::move(result), _images[valid]), + std::move(bytes)); } if (validSize == PhotoSize::Large && goodFor == validSize) { _owner->photoLoadDone(this); @@ -382,14 +383,15 @@ void PhotoData::updateImages( owner().cache(), Data::kImageCacheTag, [=](Data::FileOrigin origin) { load(size, origin); }, - [=](QImage preloaded) { + [=](QImage preloaded, QByteArray bytes) { if (const auto media = activeMediaView()) { media->set( size, size, ValidatePhotoImage( std::move(preloaded), - _images[index])); + _images[index]), + std::move(bytes)); } }); }; diff --git a/Telegram/SourceFiles/data/data_photo_media.cpp b/Telegram/SourceFiles/data/data_photo_media.cpp index 620d7429e..38260b947 100644 --- a/Telegram/SourceFiles/data/data_photo_media.cpp +++ b/Telegram/SourceFiles/data/data_photo_media.cpp @@ -49,16 +49,31 @@ Image *PhotoMedia::thumbnailInline() const { } Image *PhotoMedia::image(PhotoSize size) const { + if (const auto resolved = resolveLoadedImage(size)) { + return resolved->data.get(); + } + return nullptr; +} + +QByteArray PhotoMedia::imageBytes(PhotoSize size) const { + if (const auto resolved = resolveLoadedImage(size)) { + return resolved->bytes; + } + return QByteArray(); +} + +auto PhotoMedia::resolveLoadedImage(PhotoSize size) const +-> const PhotoImage * { const auto &original = _images[PhotoSizeIndex(size)]; if (const auto image = original.data.get()) { if (original.goodFor >= size) { - return image; + return &original; } } const auto &valid = _images[_owner->validSizeIndex(size)]; if (const auto image = valid.data.get()) { if (valid.goodFor >= size) { - return image; + return &valid; } } return nullptr; @@ -80,7 +95,11 @@ QSize PhotoMedia::size(PhotoSize size) const { return { location.width(), location.height() }; } -void PhotoMedia::set(PhotoSize size, PhotoSize goodFor, QImage image) { +void PhotoMedia::set( + PhotoSize size, + PhotoSize goodFor, + QImage image, + QByteArray bytes) { const auto index = PhotoSizeIndex(size); const auto limit = PhotoData::SideLimit(); if (image.width() > limit || image.height() > limit) { @@ -92,6 +111,7 @@ void PhotoMedia::set(PhotoSize size, PhotoSize goodFor, QImage image) { } _images[index] = PhotoImage{ .data = std::make_unique(std::move(image)), + .bytes = std::move(bytes), .goodFor = goodFor, }; _owner->session().notifyDownloaderTaskFinished(); diff --git a/Telegram/SourceFiles/data/data_photo_media.h b/Telegram/SourceFiles/data/data_photo_media.h index 7a8b095d9..ff7bd0376 100644 --- a/Telegram/SourceFiles/data/data_photo_media.h +++ b/Telegram/SourceFiles/data/data_photo_media.h @@ -24,9 +24,14 @@ public: [[nodiscard]] Image *thumbnailInline() const; [[nodiscard]] Image *image(PhotoSize size) const; + [[nodiscard]] QByteArray imageBytes(PhotoSize size) const; [[nodiscard]] QSize size(PhotoSize size) const; void wanted(PhotoSize size, Data::FileOrigin origin); - void set(PhotoSize size, PhotoSize goodFor, QImage image); + void set( + PhotoSize size, + PhotoSize goodFor, + QImage image, + QByteArray bytes); [[nodiscard]] QByteArray videoContent() const; [[nodiscard]] QSize videoSize() const; @@ -45,9 +50,12 @@ public: private: struct PhotoImage { std::unique_ptr data; + QByteArray bytes; PhotoSize goodFor = PhotoSize(); }; + const PhotoImage *resolveLoadedImage(PhotoSize size) const; + // NB! Right now DocumentMedia can outlive Main::Session! // In DocumentData::collectLocalData a shared_ptr is sent on_main. // In case this is a problem the ~Gif code should be rewritten. diff --git a/Telegram/SourceFiles/media/view/media_view_overlay_widget.cpp b/Telegram/SourceFiles/media/view/media_view_overlay_widget.cpp index ba4fb5871..3e19f6e28 100644 --- a/Telegram/SourceFiles/media/view/media_view_overlay_widget.cpp +++ b/Telegram/SourceFiles/media/view/media_view_overlay_widget.cpp @@ -1605,9 +1605,12 @@ void OverlayWidget::saveAs() { return; } - const auto image = _photoMedia->image(Data::PhotoSize::Large)->original(); + const auto image = _photoMedia->image( + Data::PhotoSize::Large)->original(); + const auto bytes = _photoMedia->imageBytes(Data::PhotoSize::Large); const auto photo = _photo; - auto filter = qsl("JPEG Image (*.jpg);;") + FileDialog::AllFilesFilter(); + const auto filter = qsl("JPEG Image (*.jpg);;") + + FileDialog::AllFilesFilter(); FileDialog::GetWritePath( _widget.get(), tr::lng_save_photo(tr::now), @@ -1620,7 +1623,7 @@ void OverlayWidget::saveAs() { _photo->date), crl::guard(_widget, [=](const QString &result) { if (!result.isEmpty() && _photo == photo) { - image.save(result, "JPG"); + saveToFile(result, bytes, image); } })); } @@ -1691,9 +1694,7 @@ void OverlayWidget::downloadMedia() { QDir().mkpath(path); } toName = filedialogDefaultName(qsl("photo"), qsl(".mp4"), path); - QFile f(toName); - if (!f.open(QIODevice::WriteOnly) - || f.write(bytes) != bytes.size()) { + if (!saveToFile(toName, bytes, QImage())) { toName = QString(); } } else { @@ -1705,14 +1706,15 @@ void OverlayWidget::downloadMedia() { _saveVisible = contentCanBeSaved(); update(_saveNav); } else { - const auto image = _photoMedia->image( - Data::PhotoSize::Large)->original(); - if (!QDir().exists(path)) { QDir().mkpath(path); } toName = filedialogDefaultName(qsl("photo"), qsl(".jpg"), path); - if (!image.save(toName, "JPG")) { + const auto saved = saveToFile( + toName, + _photoMedia->imageBytes(Data::PhotoSize::Large), + _photoMedia->image(Data::PhotoSize::Large)->original()); + if (!saved) { toName = QString(); } } @@ -1725,6 +1727,20 @@ void OverlayWidget::downloadMedia() { } } +bool OverlayWidget::saveToFile( + const QString &path, + const QByteArray &bytes, + const QImage &fallback) { + if (!bytes.isEmpty()) { + QFile f(path); + return f.open(QIODevice::WriteOnly) + && (f.write(bytes) == bytes.size()); + } else if (!fallback.isNull()) { + return fallback.save(path, "JPG"); + } + return false; +} + void OverlayWidget::saveCancel() { if (_document && _document->loading()) { _document->cancel(); diff --git a/Telegram/SourceFiles/media/view/media_view_overlay_widget.h b/Telegram/SourceFiles/media/view/media_view_overlay_widget.h index 49ab57655..6d86f20e7 100644 --- a/Telegram/SourceFiles/media/view/media_view_overlay_widget.h +++ b/Telegram/SourceFiles/media/view/media_view_overlay_widget.h @@ -417,6 +417,11 @@ private: void applyHideWindowWorkaround(); + bool saveToFile( + const QString &path, + const QByteArray &bytes, + const QImage &fallback); + Window::SessionController *findWindow(bool switchTo = true) const; bool _opengl = false;