mirror of
https://github.com/telegramdesktop/tdesktop
synced 2025-01-02 12:42:16 +00:00
Fix preloading in media viewer.
This commit is contained in:
parent
056945d9f5
commit
50e0c3ee4d
@ -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
|
@ -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
|
||||
|
@ -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 {
|
||||
|
@ -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; };
|
||||
|
@ -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()) {
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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(
|
||||
|
Loading…
Reference in New Issue
Block a user