Save original bytes of a photo on "Save to disk".

This commit is contained in:
John Preston 2022-02-21 17:23:49 +03:00
parent c39f15bd71
commit 7718764294
8 changed files with 80 additions and 29 deletions

View file

@ -64,7 +64,7 @@ void CloudImage::set(
session->data().cache(), session->data().cache(),
kImageCacheTag, kImageCacheTag,
[=](FileOrigin origin) { load(session, origin); }, [=](FileOrigin origin) { load(session, origin); },
[=](QImage preloaded) { [=](QImage preloaded, QByteArray) {
if (const auto view = activeView()) { if (const auto view = activeView()) {
view->set(session, data.preloaded); view->set(session, data.preloaded);
} }
@ -80,7 +80,7 @@ void CloudImage::update(
session->data().cache(), session->data().cache(),
kImageCacheTag, kImageCacheTag,
[=](FileOrigin origin) { load(session, origin); }, [=](FileOrigin origin) { load(session, origin); },
[=](QImage preloaded) { [=](QImage preloaded, QByteArray) {
if (const auto view = activeView()) { if (const auto view = activeView()) {
view->set(session, data.preloaded); view->set(session, data.preloaded);
} }
@ -113,7 +113,7 @@ void CloudImage::load(not_null<Main::Session*> session, FileOrigin origin) {
} }
return !(_file.flags & CloudFile::Flag::Loaded); return !(_file.flags & CloudFile::Flag::Loaded);
}; };
const auto done = [=](QImage result) { const auto done = [=](QImage result, QByteArray) {
if (const auto active = activeView()) { if (const auto active = activeView()) {
active->set(session, std::move(result)); active->set(session, std::move(result));
} }
@ -164,7 +164,7 @@ void UpdateCloudFile(
Storage::Cache::Database &cache, Storage::Cache::Database &cache,
uint8 cacheTag, uint8 cacheTag,
Fn<void(FileOrigin)> restartLoader, Fn<void(FileOrigin)> restartLoader,
Fn<void(QImage)> usePreloaded) { Fn<void(QImage, QByteArray)> usePreloaded) {
if (!data.location.valid()) { if (!data.location.valid()) {
if (data.progressivePartSize && !file.location.valid()) { if (data.progressivePartSize && !file.location.valid()) {
file.progressivePartSize = data.progressivePartSize; file.progressivePartSize = data.progressivePartSize;
@ -213,7 +213,7 @@ void UpdateCloudFile(
if (!data.preloaded.isNull()) { if (!data.preloaded.isNull()) {
file.loader = nullptr; file.loader = nullptr;
if (usePreloaded) { if (usePreloaded) {
usePreloaded(data.preloaded); usePreloaded(data.preloaded, data.bytes);
} }
} else if (file.loader) { } else if (file.loader) {
const auto origin = base::take(file.loader)->fileOrigin(); const auto origin = base::take(file.loader)->fileOrigin();
@ -307,7 +307,7 @@ void LoadCloudFile(
bool autoLoading, bool autoLoading,
uint8 cacheTag, uint8 cacheTag,
Fn<bool()> finalCheck, Fn<bool()> finalCheck,
Fn<void(QImage)> done, Fn<void(QImage, QByteArray)> done,
Fn<void(bool)> fail, Fn<void(bool)> fail,
Fn<void()> progress, Fn<void()> progress,
int downloadFrontPartSize) { int downloadFrontPartSize) {
@ -318,7 +318,7 @@ void LoadCloudFile(
onstack(true); onstack(true);
} }
} else if (const auto onstack = done) { } else if (const auto onstack = done) {
onstack(std::move(read)); onstack(std::move(read), file.loader->bytes());
} }
}; };
LoadCloudFile( LoadCloudFile(

View file

@ -96,7 +96,7 @@ void UpdateCloudFile(
Storage::Cache::Database &cache, Storage::Cache::Database &cache,
uint8 cacheTag, uint8 cacheTag,
Fn<void(FileOrigin)> restartLoader, Fn<void(FileOrigin)> restartLoader,
Fn<void(QImage)> usePreloaded = nullptr); Fn<void(QImage, QByteArray)> usePreloaded = nullptr);
void LoadCloudFile( void LoadCloudFile(
not_null<Main::Session*> session, not_null<Main::Session*> session,
@ -106,7 +106,7 @@ void LoadCloudFile(
bool autoLoading, bool autoLoading,
uint8 cacheTag, uint8 cacheTag,
Fn<bool()> finalCheck, Fn<bool()> finalCheck,
Fn<void(QImage)> done, Fn<void(QImage, QByteArray)> done,
Fn<void(bool)> fail = nullptr, Fn<void(bool)> fail = nullptr,
Fn<void()> progress = nullptr, Fn<void()> progress = nullptr,
int downloadFrontPartSize = 0); int downloadFrontPartSize = 0);

View file

@ -480,7 +480,7 @@ void DocumentData::updateThumbnails(
owner().cache(), owner().cache(),
Data::kImageCacheTag, Data::kImageCacheTag,
[&](Data::FileOrigin origin) { loadThumbnail(origin); }, [&](Data::FileOrigin origin) { loadThumbnail(origin); },
[&](QImage preloaded) { [&](QImage preloaded, QByteArray) {
if (const auto media = activeMediaView()) { if (const auto media = activeMediaView()) {
media->setThumbnail(std::move(preloaded)); media->setThumbnail(std::move(preloaded));
} }
@ -530,7 +530,7 @@ void DocumentData::loadThumbnail(Data::FileOrigin origin) {
} }
return true; return true;
}; };
const auto done = [=](QImage result) { const auto done = [=](QImage result, QByteArray) {
if (const auto active = activeMediaView()) { if (const auto active = activeMediaView()) {
active->setThumbnail(std::move(result)); active->setThumbnail(std::move(result));
} }

View file

@ -296,7 +296,7 @@ void PhotoData::load(
} }
return true; return true;
}; };
const auto done = [=](QImage result) { const auto done = [=](QImage result, QByteArray bytes) {
Expects(_images[valid].loader != nullptr); Expects(_images[valid].loader != nullptr);
// Find out what progressive photo size have we loaded exactly. // Find out what progressive photo size have we loaded exactly.
@ -316,7 +316,8 @@ void PhotoData::load(
active->set( active->set(
validSize, validSize,
goodFor, goodFor,
ValidatePhotoImage(std::move(result), _images[valid])); ValidatePhotoImage(std::move(result), _images[valid]),
std::move(bytes));
} }
if (validSize == PhotoSize::Large && goodFor == validSize) { if (validSize == PhotoSize::Large && goodFor == validSize) {
_owner->photoLoadDone(this); _owner->photoLoadDone(this);
@ -382,14 +383,15 @@ void PhotoData::updateImages(
owner().cache(), owner().cache(),
Data::kImageCacheTag, Data::kImageCacheTag,
[=](Data::FileOrigin origin) { load(size, origin); }, [=](Data::FileOrigin origin) { load(size, origin); },
[=](QImage preloaded) { [=](QImage preloaded, QByteArray bytes) {
if (const auto media = activeMediaView()) { if (const auto media = activeMediaView()) {
media->set( media->set(
size, size,
size, size,
ValidatePhotoImage( ValidatePhotoImage(
std::move(preloaded), std::move(preloaded),
_images[index])); _images[index]),
std::move(bytes));
} }
}); });
}; };

View file

@ -49,16 +49,31 @@ Image *PhotoMedia::thumbnailInline() const {
} }
Image *PhotoMedia::image(PhotoSize size) 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)]; const auto &original = _images[PhotoSizeIndex(size)];
if (const auto image = original.data.get()) { if (const auto image = original.data.get()) {
if (original.goodFor >= size) { if (original.goodFor >= size) {
return image; return &original;
} }
} }
const auto &valid = _images[_owner->validSizeIndex(size)]; const auto &valid = _images[_owner->validSizeIndex(size)];
if (const auto image = valid.data.get()) { if (const auto image = valid.data.get()) {
if (valid.goodFor >= size) { if (valid.goodFor >= size) {
return image; return &valid;
} }
} }
return nullptr; return nullptr;
@ -80,7 +95,11 @@ QSize PhotoMedia::size(PhotoSize size) const {
return { location.width(), location.height() }; 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 index = PhotoSizeIndex(size);
const auto limit = PhotoData::SideLimit(); const auto limit = PhotoData::SideLimit();
if (image.width() > limit || image.height() > limit) { if (image.width() > limit || image.height() > limit) {
@ -92,6 +111,7 @@ void PhotoMedia::set(PhotoSize size, PhotoSize goodFor, QImage image) {
} }
_images[index] = PhotoImage{ _images[index] = PhotoImage{
.data = std::make_unique<Image>(std::move(image)), .data = std::make_unique<Image>(std::move(image)),
.bytes = std::move(bytes),
.goodFor = goodFor, .goodFor = goodFor,
}; };
_owner->session().notifyDownloaderTaskFinished(); _owner->session().notifyDownloaderTaskFinished();

View file

@ -24,9 +24,14 @@ public:
[[nodiscard]] Image *thumbnailInline() const; [[nodiscard]] Image *thumbnailInline() const;
[[nodiscard]] Image *image(PhotoSize size) const; [[nodiscard]] Image *image(PhotoSize size) const;
[[nodiscard]] QByteArray imageBytes(PhotoSize size) const;
[[nodiscard]] QSize size(PhotoSize size) const; [[nodiscard]] QSize size(PhotoSize size) const;
void wanted(PhotoSize size, Data::FileOrigin origin); 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]] QByteArray videoContent() const;
[[nodiscard]] QSize videoSize() const; [[nodiscard]] QSize videoSize() const;
@ -45,9 +50,12 @@ public:
private: private:
struct PhotoImage { struct PhotoImage {
std::unique_ptr<Image> data; std::unique_ptr<Image> data;
QByteArray bytes;
PhotoSize goodFor = PhotoSize(); PhotoSize goodFor = PhotoSize();
}; };
const PhotoImage *resolveLoadedImage(PhotoSize size) const;
// NB! Right now DocumentMedia can outlive Main::Session! // NB! Right now DocumentMedia can outlive Main::Session!
// In DocumentData::collectLocalData a shared_ptr is sent on_main. // In DocumentData::collectLocalData a shared_ptr is sent on_main.
// In case this is a problem the ~Gif code should be rewritten. // In case this is a problem the ~Gif code should be rewritten.

View file

@ -1605,9 +1605,12 @@ void OverlayWidget::saveAs() {
return; 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; const auto photo = _photo;
auto filter = qsl("JPEG Image (*.jpg);;") + FileDialog::AllFilesFilter(); const auto filter = qsl("JPEG Image (*.jpg);;")
+ FileDialog::AllFilesFilter();
FileDialog::GetWritePath( FileDialog::GetWritePath(
_widget.get(), _widget.get(),
tr::lng_save_photo(tr::now), tr::lng_save_photo(tr::now),
@ -1620,7 +1623,7 @@ void OverlayWidget::saveAs() {
_photo->date), _photo->date),
crl::guard(_widget, [=](const QString &result) { crl::guard(_widget, [=](const QString &result) {
if (!result.isEmpty() && _photo == photo) { if (!result.isEmpty() && _photo == photo) {
image.save(result, "JPG"); saveToFile(result, bytes, image);
} }
})); }));
} }
@ -1691,9 +1694,7 @@ void OverlayWidget::downloadMedia() {
QDir().mkpath(path); QDir().mkpath(path);
} }
toName = filedialogDefaultName(qsl("photo"), qsl(".mp4"), path); toName = filedialogDefaultName(qsl("photo"), qsl(".mp4"), path);
QFile f(toName); if (!saveToFile(toName, bytes, QImage())) {
if (!f.open(QIODevice::WriteOnly)
|| f.write(bytes) != bytes.size()) {
toName = QString(); toName = QString();
} }
} else { } else {
@ -1705,14 +1706,15 @@ void OverlayWidget::downloadMedia() {
_saveVisible = contentCanBeSaved(); _saveVisible = contentCanBeSaved();
update(_saveNav); update(_saveNav);
} else { } else {
const auto image = _photoMedia->image(
Data::PhotoSize::Large)->original();
if (!QDir().exists(path)) { if (!QDir().exists(path)) {
QDir().mkpath(path); QDir().mkpath(path);
} }
toName = filedialogDefaultName(qsl("photo"), qsl(".jpg"), 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(); 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() { void OverlayWidget::saveCancel() {
if (_document && _document->loading()) { if (_document && _document->loading()) {
_document->cancel(); _document->cancel();

View file

@ -417,6 +417,11 @@ private:
void applyHideWindowWorkaround(); void applyHideWindowWorkaround();
bool saveToFile(
const QString &path,
const QByteArray &bytes,
const QImage &fallback);
Window::SessionController *findWindow(bool switchTo = true) const; Window::SessionController *findWindow(bool switchTo = true) const;
bool _opengl = false; bool _opengl = false;