Fix preloading in media viewer.

This commit is contained in:
John Preston 2020-05-27 13:01:25 +04:00
parent 056945d9f5
commit 50e0c3ee4d
7 changed files with 209 additions and 174 deletions

View File

@ -17,6 +17,7 @@ void UpdateCloudFile(
CloudFile &file,
const ImageWithLocation &data,
Storage::Cache::Database &cache,
uint8 cacheTag,
Fn<void(FileOrigin)> restartLoader,
Fn<void(QImage)> usePreloaded) {
if (!data.location.valid()) {
@ -42,7 +43,7 @@ void UpdateCloudFile(
cacheKey,
Storage::Cache::Database::TaggedValue(
std::move(cacheBytes),
Data::kImageCacheTag));
cacheTag));
}
}
file.location = data.location;
@ -58,4 +59,132 @@ void UpdateCloudFile(
}
}
void LoadCloudFile(
CloudFile &file,
FileOrigin origin,
LoadFromCloudSetting fromCloud,
bool autoLoading,
uint8 cacheTag,
Fn<bool()> finalCheck,
Fn<void(CloudFile&)> done,
Fn<void(bool)> fail,
Fn<void()> progress) {
if (file.loader) {
if (fromCloud == LoadFromCloudOrLocal) {
file.loader->permitLoadFromCloud();
}
return;
} else if ((file.flags & CloudFile::Flag::Failed)
|| !file.location.valid()
|| (finalCheck && !finalCheck())) {
return;
}
file.flags &= ~CloudFile::Flag::Cancelled;
file.loader = CreateFileLoader(
file.location.file(),
origin,
QString(),
file.byteSize,
UnknownFileLocation,
LoadToCacheAsWell,
fromCloud,
autoLoading,
cacheTag);
const auto finish = [done](CloudFile &file) {
if (!file.loader || file.loader->cancelled()) {
file.flags |= CloudFile::Flag::Cancelled;
} else {
done(file);
}
// NB! file.loader may be in ~FileLoader() already.
if (const auto loader = base::take(file.loader)) {
if (file.flags & CloudFile::Flag::Cancelled) {
loader->cancel();
}
}
};
file.loader->updates(
) | rpl::start_with_next_error_done([=] {
if (const auto onstack = progress) {
onstack();
}
}, [=, &file](bool started) {
finish(file);
file.flags |= CloudFile::Flag::Failed;
if (const auto onstack = fail) {
onstack(started);
}
}, [=, &file] {
finish(file);
}, file.loader->lifetime());
file.loader->start();
}
void LoadCloudFile(
CloudFile &file,
FileOrigin origin,
LoadFromCloudSetting fromCloud,
bool autoLoading,
uint8 cacheTag,
Fn<bool()> finalCheck,
Fn<void(QImage)> done,
Fn<void(bool)> fail,
Fn<void()> progress) {
const auto callback = [=](CloudFile &file) {
if (auto read = file.loader->imageData(); read.isNull()) {
file.flags |= CloudFile::Flag::Failed;
if (const auto onstack = fail) {
onstack(true);
}
} else if (const auto onstack = done) {
onstack(std::move(read));
}
};
LoadCloudFile(
file,
origin,
fromCloud,
autoLoading,
cacheTag,
finalCheck,
callback,
std::move(fail),
std::move(progress));
}
void LoadCloudFile(
CloudFile &file,
FileOrigin origin,
LoadFromCloudSetting fromCloud,
bool autoLoading,
uint8 cacheTag,
Fn<bool()> finalCheck,
Fn<void(QByteArray)> done,
Fn<void(bool)> fail,
Fn<void()> progress) {
const auto callback = [=](CloudFile &file) {
if (auto bytes = file.loader->bytes(); bytes.isEmpty()) {
file.flags |= Data::CloudFile::Flag::Failed;
if (const auto onstack = fail) {
onstack(true);
}
} else if (const auto onstack = done) {
onstack(std::move(bytes));
}
};
LoadCloudFile(
file,
origin,
fromCloud,
autoLoading,
cacheTag,
finalCheck,
callback,
std::move(fail),
std::move(progress));
}
} // namespace Data

View File

@ -39,7 +39,30 @@ void UpdateCloudFile(
CloudFile &file,
const ImageWithLocation &data,
Storage::Cache::Database &cache,
uint8 cacheTag,
Fn<void(FileOrigin)> restartLoader,
Fn<void(QImage)> usePreloaded = nullptr);
void LoadCloudFile(
CloudFile &file,
FileOrigin origin,
LoadFromCloudSetting fromCloud,
bool autoLoading,
uint8 cacheTag,
Fn<bool()> finalCheck,
Fn<void(QImage)> done,
Fn<void(bool)> fail = nullptr,
Fn<void()> progress = nullptr);
void LoadCloudFile(
CloudFile &file,
FileOrigin origin,
LoadFromCloudSetting fromCloud,
bool autoLoading,
uint8 cacheTag,
Fn<bool()> finalCheck,
Fn<void(QByteArray)> done,
Fn<void(bool)> fail = nullptr,
Fn<void()> progress = nullptr);
} // namespace Data

View File

@ -617,6 +617,7 @@ void DocumentData::updateThumbnails(
_thumbnail,
thumbnail,
owner().cache(),
Data::kImageCacheTag,
[&](Data::FileOrigin origin) { loadThumbnail(origin); },
[&](QImage preloaded) {
if (const auto media = activeMediaView()) {
@ -627,6 +628,7 @@ void DocumentData::updateThumbnails(
_videoThumbnail,
videoThumbnail,
owner().cache(),
Data::kAnimationCacheTag,
[&](Data::FileOrigin origin) { loadVideoThumbnail(origin); });
}
@ -647,47 +649,24 @@ bool DocumentData::thumbnailLoading() const {
}
bool DocumentData::thumbnailFailed() const {
return (_flags & Flag::ThumbnailFailed);
return (_thumbnail.flags & Data::CloudFile::Flag::Failed);
}
void DocumentData::loadThumbnail(Data::FileOrigin origin) {
if (_thumbnail.loader
|| (_flags & Flag::ThumbnailFailed)
|| !_thumbnail.location.valid()) {
return;
} else if (const auto active = activeMediaView()) {
if (active->thumbnail()) {
return;
}
}
auto &file = _thumbnail;
const auto fromCloud = LoadFromCloudOrLocal;
const auto cacheTag = Data::kImageCacheTag;
const auto autoLoading = false;
_thumbnail.loader = CreateFileLoader(
_thumbnail.location.file(),
origin,
QString(),
_thumbnail.byteSize,
UnknownFileLocation,
LoadToCacheAsWell,
LoadFromCloudOrLocal,
autoLoading,
Data::kImageCacheTag);
_thumbnail.loader->updates(
) | rpl::start_with_error_done([=](bool started) {
_thumbnail.loader = nullptr;
_flags |= Flag::ThumbnailFailed;
}, [=] {
if (_thumbnail.loader && !_thumbnail.loader->cancelled()) {
if (auto read = _thumbnail.loader->imageData(); read.isNull()) {
_flags |= Flag::ThumbnailFailed;
} else if (const auto active = activeMediaView()) {
active->setThumbnail(std::move(read));
}
Data::LoadCloudFile(file, origin, fromCloud, autoLoading, cacheTag, [=] {
if (const auto active = activeMediaView()) {
return !active->thumbnail();
}
_thumbnail.loader = nullptr;
}, _thumbnail.loader->lifetime());
_thumbnail.loader->start();
return true;
}, [=](QImage result) {
if (const auto active = activeMediaView()) {
active->setThumbnail(std::move(result));
}
});
}
const ImageLocation &DocumentData::thumbnailLocation() const {
@ -707,48 +686,24 @@ bool DocumentData::videoThumbnailLoading() const {
}
bool DocumentData::videoThumbnailFailed() const {
return (_flags & Flag::VideoThumbnailFailed);
return (_videoThumbnail.flags & Data::CloudFile::Flag::Failed);
}
void DocumentData::loadVideoThumbnail(Data::FileOrigin origin) {
if (_videoThumbnail.loader
|| (_flags & Flag::VideoThumbnailFailed)
|| !_videoThumbnail.location.valid()) {
return;
} else if (const auto active = activeMediaView()) {
if (!active->videoThumbnailContent().isEmpty()) {
return;
}
}
auto &file = _videoThumbnail;
const auto fromCloud = LoadFromCloudOrLocal;
const auto cacheTag = Data::kAnimationCacheTag;
const auto autoLoading = false;
_videoThumbnail.loader = CreateFileLoader(
_videoThumbnail.location.file(),
origin,
QString(),
_videoThumbnail.byteSize,
UnknownFileLocation,
LoadToCacheAsWell,
LoadFromCloudOrLocal,
autoLoading,
Data::kAnimationCacheTag);
_videoThumbnail.loader->updates(
) | rpl::start_with_error_done([=](bool started) {
_videoThumbnail.loader = nullptr;
_flags |= Flag::VideoThumbnailFailed;
}, [=] {
if (_videoThumbnail.loader && !_videoThumbnail.loader->cancelled()) {
auto bytes = _videoThumbnail.loader->bytes();
if (bytes.isEmpty()) {
_flags |= Flag::VideoThumbnailFailed;
} else if (const auto active = activeMediaView()) {
active->setVideoThumbnail(std::move(bytes));
}
Data::LoadCloudFile(file, origin, fromCloud, autoLoading, cacheTag, [=] {
if (const auto active = activeMediaView()) {
return active->videoThumbnailContent().isEmpty();
}
_videoThumbnail.loader = nullptr;
}, _videoThumbnail.loader->lifetime());
_videoThumbnail.loader->start();
return true;
}, [=](QByteArray result) {
if (const auto active = activeMediaView()) {
active->setVideoThumbnail(std::move(result));
}
});
}
const ImageLocation &DocumentData::videoThumbnailLocation() const {

View File

@ -257,8 +257,6 @@ private:
ImageType = 0x08,
DownloadCancelled = 0x10,
LoadedInMediaCache = 0x20,
ThumbnailFailed = 0x40,
VideoThumbnailFailed = 0x80,
};
using Flags = base::flags<Flag>;
friend constexpr bool is_flag_type(Flag) { return true; };

View File

@ -127,14 +127,9 @@ bool PhotoData::displayLoading() const {
}
void PhotoData::cancel() {
if (!loading()) {
return;
if (loading()) {
_images[PhotoSizeIndex(PhotoSize::Large)].loader->cancel();
}
const auto index = PhotoSizeIndex(PhotoSize::Large);
_images[index].flags |= Data::CloudFile::Flag::Cancelled;
destroyLoader(PhotoSize::Large);
_owner->photoLoadDone(this);
}
float64 PhotoData::progress() const {
@ -244,91 +239,37 @@ void PhotoData::load(
bool autoLoading) {
const auto index = validSizeIndex(size);
auto &image = _images[index];
if (image.loader) {
if (fromCloud == LoadFromCloudOrLocal) {
image.loader->permitLoadFromCloud();
}
return;
} else if ((image.flags & Data::CloudFile::Flag::Failed)
|| !image.location.valid()) {
return;
} else if (const auto active = activeMediaView()) {
if (active->image(size)) {
return;
}
}
// Could've changed, if the requested size didn't have a location.
size = static_cast<PhotoSize>(index);
image.flags &= ~Data::CloudFile::Flag::Cancelled;
image.loader = CreateFileLoader(
image.location.file(),
origin,
QString(),
image.byteSize,
UnknownFileLocation,
LoadToCacheAsWell,
fromCloud,
autoLoading,
Data::kImageCacheTag);
image.loader->updates(
) | rpl::start_with_next_error_done([=] {
if (size == PhotoSize::Large) {
_owner->photoLoadProgress(this);
const auto loadingSize = static_cast<PhotoSize>(index);
const auto cacheTag = Data::kImageCacheTag;
Data::LoadCloudFile(image, origin, fromCloud, autoLoading, cacheTag, [=] {
if (const auto active = activeMediaView()) {
return !active->image(size);
}
}, [=, &image](bool started) {
finishLoad(size);
image.flags |= Data::CloudFile::Flag::Failed;
if (size == PhotoSize::Large) {
return true;
}, [=](QImage result) {
if (const auto active = activeMediaView()) {
active->set(loadingSize, std::move(result));
}
if (loadingSize == PhotoSize::Large) {
_owner->photoLoadDone(this);
}
}, [=](bool started) {
if (loadingSize == PhotoSize::Large) {
_owner->photoLoadFail(this, started);
}
}, [=] {
finishLoad(size);
if (size == PhotoSize::Large) {
_owner->photoLoadDone(this);
if (loadingSize == PhotoSize::Large) {
_owner->photoLoadProgress(this);
}
}, image.loader->lifetime());
image.loader->start();
});
if (size == PhotoSize::Large) {
_owner->notifyPhotoLayoutChanged(this);
}
}
void PhotoData::finishLoad(PhotoSize size) {
const auto index = PhotoSizeIndex(size);
auto &image = _images[index];
// NB! image.loader may be in ~FileLoader() already.
const auto guard = gsl::finally([&] {
destroyLoader(size);
});
if (!image.loader || image.loader->cancelled()) {
image.flags |= Data::CloudFile::Flag::Cancelled;
return;
} else if (auto read = image.loader->imageData(); read.isNull()) {
image.flags |= Data::CloudFile::Flag::Failed;
} else if (const auto active = activeMediaView()) {
active->set(size, std::move(read));
}
}
void PhotoData::destroyLoader(PhotoSize size) {
const auto index = PhotoSizeIndex(size);
auto &image = _images[index];
// NB! image.loader may be in ~FileLoader() already.
if (!image.loader) {
return;
}
const auto loader = base::take(image.loader);
if (image.flags & Data::CloudFile::Flag::Cancelled) {
loader->cancel();
}
}
std::shared_ptr<PhotoMedia> PhotoData::createMediaView() {
if (auto result = activeMediaView()) {
return result;
@ -356,6 +297,7 @@ void PhotoData::updateImages(
_images[PhotoSizeIndex(size)],
data,
owner().cache(),
Data::kImageCacheTag,
[=](Data::FileOrigin origin) { load(size, origin); },
[=](QImage preloaded) {
if (const auto media = activeMediaView()) {

View File

@ -124,9 +124,6 @@ public:
std::unique_ptr<Data::UploadState> uploadingData;
private:
void finishLoad(Data::PhotoSize size);
void destroyLoader(Data::PhotoSize size);
QByteArray _inlineThumbnailBytes;
std::array<Data::CloudFile, Data::kPhotoSizeCount> _images;

View File

@ -71,7 +71,7 @@ namespace Media {
namespace View {
namespace {
constexpr auto kPreloadCount = 4;
constexpr auto kPreloadCount = 3;
constexpr auto kMaxZoomLevel = 7; // x8
constexpr auto kZoomToScreenLevel = 1024;
constexpr auto kOverlayLoaderPriority = 2;
@ -939,7 +939,7 @@ bool OverlayWidget::radialLoading() const {
if (_document) {
return _document->loading() && !_streamed;
} else if (_photo) {
return _photo->loading();
return _photo->displayLoading();
}
return false;
}
@ -1122,8 +1122,8 @@ void OverlayWidget::assignMediaPointer(not_null<PhotoData*> photo) {
if (_photo != photo) {
_photo = photo;
_photoMedia = _photo->createMediaView();
_photoMedia->wanted(Data::PhotoSize::Large, fileOrigin());
_photoMedia->wanted(Data::PhotoSize::Small, fileOrigin());
_photo->load(fileOrigin(), LoadFromCloudOrLocal, true);
}
}
@ -1739,7 +1739,6 @@ void OverlayWidget::refreshMediaViewer() {
}
findCurrent();
updateControls();
preloadData(0);
}
void OverlayWidget::refreshFromLabel(HistoryItem *item) {
@ -3463,7 +3462,7 @@ bool OverlayWidget::moveToNext(int delta) {
return false;
}
auto newIndex = *_index + delta;
return moveToEntity(entityByIndex(newIndex));
return moveToEntity(entityByIndex(newIndex), delta);
}
bool OverlayWidget::moveToEntity(const Entity &entity, int preloadDelta) {
@ -3498,22 +3497,14 @@ void OverlayWidget::preloadData(int delta) {
auto till = *_index + (delta ? delta * kPreloadCount : 1);
if (from > till) std::swap(from, till);
if (delta != 0) {
auto forgetIndex = *_index - delta * 2;
auto entity = entityByIndex(forgetIndex);
if (auto photo = base::get_if<not_null<PhotoData*>>(&entity.data)) {
(*photo)->unload();
}
}
auto photos = base::flat_set<std::shared_ptr<Data::PhotoMedia>>();
auto documents = base::flat_set<std::shared_ptr<Data::DocumentMedia>>();
for (auto index = from; index != till; ++index) {
for (auto index = from; index != till + 1; ++index) {
auto entity = entityByIndex(index);
if (auto photo = base::get_if<not_null<PhotoData*>>(&entity.data)) {
const auto [i, ok] = photos.emplace((*photo)->createMediaView());
(*i)->wanted(Data::PhotoSize::Small, fileOrigin());
(*i)->wanted(Data::PhotoSize::Large, fileOrigin());
(*photo)->load(fileOrigin(), LoadFromCloudOrLocal, true);
} else if (auto document = base::get_if<not_null<DocumentData*>>(
&entity.data)) {
const auto [i, ok] = documents.emplace(