Try using progressive jpeg photo size.

This commit is contained in:
John Preston 2020-08-25 22:03:41 +04:00
parent 55edb3bdfe
commit 0888901d79
17 changed files with 198 additions and 63 deletions

View File

@ -1082,7 +1082,8 @@ void Call::handleControllerError(const QString &error) {
void Call::destroyController() {
if (_instance) {
_instance->stop();
_instance->stop([](tgcalls::FinalState) {
});
DEBUG_LOG(("Call Info: Destroying call controller.."));
_instance.reset();

View File

@ -160,6 +160,9 @@ void UpdateCloudFile(
Fn<void(FileOrigin)> restartLoader,
Fn<void(QImage)> usePreloaded) {
if (!data.location.valid()) {
if (data.progressivePartSize && !file.location.valid()) {
file.progressivePartSize = data.progressivePartSize;
}
return;
}
@ -208,11 +211,18 @@ void LoadCloudFile(
Fn<bool()> finalCheck,
Fn<void(CloudFile&)> done,
Fn<void(bool)> fail,
Fn<void()> progress) {
Fn<void()> progress,
int downloadFrontPartSize = 0) {
const auto loadSize = downloadFrontPartSize
? downloadFrontPartSize
: file.byteSize;
if (file.loader) {
if (fromCloud == LoadFromCloudOrLocal) {
file.loader->permitLoadFromCloud();
}
if (file.loader->fullSize() < loadSize) {
file.loader->increaseLoadSize(loadSize);
}
return;
} else if ((file.flags & CloudFile::Flag::Failed)
|| !file.location.valid()
@ -225,7 +235,7 @@ void LoadCloudFile(
file.location.file(),
origin,
QString(),
file.byteSize,
loadSize,
UnknownFileLocation,
LoadToCacheAsWell,
fromCloud,
@ -275,7 +285,8 @@ void LoadCloudFile(
Fn<bool()> finalCheck,
Fn<void(QImage)> done,
Fn<void(bool)> fail,
Fn<void()> progress) {
Fn<void()> progress,
int downloadFrontPartSize) {
const auto callback = [=](CloudFile &file) {
if (auto read = file.loader->imageData(); read.isNull()) {
file.flags |= CloudFile::Flag::Failed;
@ -296,7 +307,8 @@ void LoadCloudFile(
finalCheck,
callback,
std::move(fail),
std::move(progress));
std::move(progress),
downloadFrontPartSize);
}
void LoadCloudFile(

View File

@ -39,6 +39,7 @@ struct CloudFile final {
ImageLocation location;
std::unique_ptr<FileLoader> loader;
int byteSize = 0;
int progressivePartSize = 0;
base::flags<Flag> flags;
};
@ -105,7 +106,8 @@ void LoadCloudFile(
Fn<bool()> finalCheck,
Fn<void(QImage)> done,
Fn<void(bool)> fail = nullptr,
Fn<void()> progress = nullptr);
Fn<void()> progress = nullptr,
int downloadFrontPartSize = 0);
void LoadCloudFile(
not_null<Main::Session*> session,

View File

@ -82,12 +82,30 @@ int PhotoData::validSizeIndex(PhotoSize size) const {
return PhotoSizeIndex(PhotoSize::Large);
}
int PhotoData::existingSizeIndex(PhotoSize size) const {
const auto index = PhotoSizeIndex(size);
for (auto i = index; i != kPhotoSizeCount; ++i) {
if (_images[i].location.valid() || _images[i].progressivePartSize) {
return i;
}
}
return PhotoSizeIndex(PhotoSize::Large);
}
bool PhotoData::hasExact(PhotoSize size) const {
return _images[PhotoSizeIndex(size)].location.valid();
}
bool PhotoData::loading(PhotoSize size) const {
return (_images[validSizeIndex(size)].loader != nullptr);
const auto valid = validSizeIndex(size);
const auto existing = existingSizeIndex(size);
if (!_images[valid].loader) {
return false;
} else if (valid == existing) {
return true;
}
return (_images[valid].loader->fullSize()
>= _images[existing].progressivePartSize);
}
bool PhotoData::failed(PhotoSize size) const {
@ -117,6 +135,10 @@ std::optional<QSize> PhotoData::size(PhotoSize size) const {
}
int PhotoData::imageByteSize(PhotoSize size) const {
const auto existing = existingSizeIndex(size);
if (const auto result = _images[existing].progressivePartSize) {
return result;
}
return _images[validSizeIndex(size)].byteSize;
}
@ -235,10 +257,12 @@ void PhotoData::load(
Data::FileOrigin origin,
LoadFromCloudSetting fromCloud,
bool autoLoading) {
const auto index = validSizeIndex(size);
const auto valid = validSizeIndex(size);
const auto existing = existingSizeIndex(size);
// Could've changed, if the requested size didn't have a location.
const auto loadingSize = static_cast<PhotoSize>(index);
const auto validSize = static_cast<PhotoSize>(valid);
const auto existingSize = static_cast<PhotoSize>(existing);
const auto finalCheck = [=] {
if (const auto active = activeMediaView()) {
return !active->image(size);
@ -247,25 +271,25 @@ void PhotoData::load(
};
const auto done = [=](QImage result) {
if (const auto active = activeMediaView()) {
active->set(loadingSize, std::move(result));
active->set(validSize, existingSize, std::move(result));
}
if (loadingSize == PhotoSize::Large) {
if (validSize == PhotoSize::Large) {
_owner->photoLoadDone(this);
}
};
const auto fail = [=](bool started) {
if (loadingSize == PhotoSize::Large) {
if (validSize == PhotoSize::Large) {
_owner->photoLoadFail(this, started);
}
};
const auto progress = [=] {
if (loadingSize == PhotoSize::Large) {
if (validSize == PhotoSize::Large) {
_owner->photoLoadProgress(this);
}
};
Data::LoadCloudFile(
&session(),
_images[index],
_images[valid],
origin,
fromCloud,
autoLoading,
@ -273,7 +297,8 @@ void PhotoData::load(
finalCheck,
done,
fail,
progress);
progress,
_images[existing].progressivePartSize);
if (size == PhotoSize::Large) {
_owner->notifyPhotoLayoutChanged(this);
@ -313,7 +338,7 @@ void PhotoData::updateImages(
[=](Data::FileOrigin origin) { load(size, origin); },
[=](QImage preloaded) {
if (const auto media = activeMediaView()) {
media->set(size, data.preloaded);
media->set(size, size, data.preloaded);
}
});
};

View File

@ -92,6 +92,7 @@ public:
const ImageWithLocation &video,
crl::time videoStartTime);
[[nodiscard]] int validSizeIndex(Data::PhotoSize size) const;
[[nodiscard]] int existingSizeIndex(Data::PhotoSize size) const;
[[nodiscard]] QByteArray inlineThumbnailBytes() const {
return _inlineThumbnailBytes;

View File

@ -49,29 +49,34 @@ Image *PhotoMedia::thumbnailInline() const {
}
Image *PhotoMedia::image(PhotoSize size) const {
if (const auto image = _images[PhotoSizeIndex(size)].get()) {
return image;
const auto &original = _images[PhotoSizeIndex(size)];
if (const auto image = original.data.get()) {
return (original.goodFor >= size) ? image : nullptr;
}
return _images[_owner->validSizeIndex(size)].get();
const auto &valid = _images[_owner->validSizeIndex(size)];
if (const auto image = valid.data.get()) {
return (valid.goodFor >= size) ? image : nullptr;
}
return nullptr;
}
void PhotoMedia::wanted(PhotoSize size, Data::FileOrigin origin) {
const auto index = _owner->validSizeIndex(size);
if (!_images[index]) {
if (!_images[index].data || _images[index].goodFor < size) {
_owner->load(size, origin);
}
}
QSize PhotoMedia::size(PhotoSize size) const {
const auto index = PhotoSizeIndex(size);
if (const auto image = _images[index].get()) {
if (const auto image = _images[index].data.get()) {
return image->size();
}
const auto &location = _owner->location(size);
return { location.width(), location.height() };
}
void PhotoMedia::set(PhotoSize size, QImage image) {
void PhotoMedia::set(PhotoSize size, PhotoSize goodFor, QImage image) {
const auto index = PhotoSizeIndex(size);
const auto limit = PhotoData::SideLimit();
if (image.width() > limit || image.height() > limit) {
@ -81,7 +86,10 @@ void PhotoMedia::set(PhotoSize size, QImage image) {
Qt::KeepAspectRatio,
Qt::SmoothTransformation);
}
_images[index] = std::make_unique<Image>(std::move(image));
_images[index] = PhotoImage{
.data = std::make_unique<Image>(std::move(image)),
.goodFor = goodFor,
};
_owner->session().notifyDownloaderTaskFinished();
}
@ -106,7 +114,8 @@ void PhotoMedia::setVideo(QByteArray content) {
bool PhotoMedia::loaded() const {
const auto index = PhotoSizeIndex(PhotoSize::Large);
return (_images[index] != nullptr);
return (_images[index].data != nullptr)
&& (_images[index].goodFor >= PhotoSize::Large);
}
float64 PhotoMedia::progress() const {
@ -136,8 +145,11 @@ void PhotoMedia::collectLocalData(not_null<PhotoMedia*> local) {
_inlineThumbnail = std::make_unique<Image>(image->original());
}
for (auto i = 0; i != kPhotoSizeCount; ++i) {
if (const auto image = local->_images[i].get()) {
_images[i] = std::make_unique<Image>(image->original());
if (const auto image = local->_images[i].data.get()) {
_images[i] = PhotoImage{
.data = std::make_unique<Image>(image->original()),
.goodFor = local->_images[i].goodFor
};
}
}
}

View File

@ -26,7 +26,7 @@ public:
[[nodiscard]] Image *image(PhotoSize size) const;
[[nodiscard]] QSize size(PhotoSize size) const;
void wanted(PhotoSize size, Data::FileOrigin origin);
void set(PhotoSize size, QImage image);
void set(PhotoSize size, PhotoSize goodFor, QImage image);
[[nodiscard]] QByteArray videoContent() const;
[[nodiscard]] QSize videoSize() const;
@ -41,12 +41,17 @@ public:
void collectLocalData(not_null<PhotoMedia*> local);
private:
struct PhotoImage {
std::unique_ptr<Image> data;
PhotoSize goodFor = PhotoSize();
};
// 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.
const not_null<PhotoData*> _owner;
mutable std::unique_ptr<Image> _inlineThumbnail;
std::array<std::unique_ptr<Image>, kPhotoSizeCount> _images;
std::array<PhotoImage, kPhotoSizeCount> _images;
QByteArray _videoBytes;
};

View File

@ -2262,6 +2262,21 @@ void Session::photoApplyFields(
not_null<PhotoData*> photo,
const MTPDphoto &data) {
const auto &sizes = data.vsizes().v;
const auto progressive = [&] {
const auto kInvalidIndex = sizes.size();
const auto area = [&](const MTPPhotoSize &size) {
return size.match([](const MTPDphotoSizeProgressive &data) {
return data.vw().v * data.vh().v;
}, [](const auto &) {
return 0;
});
};
const auto found = ranges::max_element(sizes, std::greater<>(), area);
return (found == sizes.end()
|| found->type() != mtpc_photoSizeProgressive)
? sizes.end()
: found;
}();
const auto find = [&](const QByteArray &levels) {
const auto kInvalidIndex = int(levels.size());
const auto level = [&](const MTPPhotoSize &size) {
@ -2300,7 +2315,10 @@ void Session::photoApplyFields(
std::greater<>(),
area);
};
const auto large = image(LargeLevels);
const auto useProgressive = (progressive != sizes.end());
const auto large = useProgressive
? Images::FromPhotoSize(_session, data, *progressive)
: image(LargeLevels);
if (large.location.valid()) {
const auto video = findVideoSize();
photoApplyFields(
@ -2311,8 +2329,12 @@ void Session::photoApplyFields(
data.vdc_id().v,
data.is_has_stickers(),
FindPhotoInlineThumbnail(data),
image(SmallLevels),
image(ThumbnailLevels),
(useProgressive
? ImageWithLocation()
: image(SmallLevels)),
(useProgressive
? Images::FromProgressiveSize(_session, *progressive, 1)
: image(ThumbnailLevels)),
large,
(video
? Images::FromVideoSize(_session, data, *video)

View File

@ -265,8 +265,11 @@ Image ParseMaxImage(
result.file.content = data.vbytes().v;
result.file.size = result.file.content.size();
} else if constexpr (MTPDphotoSizeProgressive::Is<decltype(data)>()) {
if (data.vsizes().v.isEmpty()) {
return;
}
result.file.content = QByteArray();
result.file.size = data.vsizes().v.size();
result.file.size = data.vsizes().v.back().v;
} else {
result.file.content = QByteArray();
result.file.size = data.vsize().v;
@ -426,8 +429,11 @@ Image ParseDocumentThumb(
result.file.content = data.vbytes().v;
result.file.size = result.file.content.size();
} else if constexpr (MTPDphotoSizeProgressive::Is<decltype(data)>()) {
if (data.vsizes().v.isEmpty()) {
return Image();
}
result.file.content = QByteArray();
result.file.size = data.vsizes().v.size();
result.file.size = data.vsizes().v.back().v;
} else {
result.file.content = QByteArray();
result.file.size = data.vsize().v;

View File

@ -145,29 +145,21 @@ void FileLoader::finishWithBytes(const QByteArray &data) {
session->notifyDownloaderTaskFinished();
}
QByteArray FileLoader::imageFormat(const QSize &shrinkBox) const {
if (_imageFormat.isEmpty() && _locationType == UnknownFileLocation) {
readImage(shrinkBox);
}
return _imageFormat;
}
QImage FileLoader::imageData(const QSize &shrinkBox) const {
QImage FileLoader::imageData(int progressiveSizeLimit) const {
if (_imageData.isNull() && _locationType == UnknownFileLocation) {
readImage(shrinkBox);
readImage(progressiveSizeLimit);
}
return _imageData;
}
void FileLoader::readImage(const QSize &shrinkBox) const {
void FileLoader::readImage(int progressiveSizeLimit) const {
const auto buffer = progressiveSizeLimit
? QByteArray::fromRawData(_data.data(), progressiveSizeLimit)
: _data;
auto format = QByteArray();
auto image = App::readImage(_data, &format, false);
auto image = App::readImage(buffer, &format, false);
if (!image.isNull()) {
if (!shrinkBox.isEmpty() && (image.width() > shrinkBox.width() || image.height() > shrinkBox.height())) {
_imageData = image.scaled(shrinkBox, Qt::KeepAspectRatio, Qt::SmoothTransformation);
} else {
_imageData = std::move(image);
}
_imageData = std::move(image);
_imageFormat = format;
}
}
@ -199,6 +191,12 @@ void FileLoader::permitLoadFromCloud() {
_fromCloud = LoadFromCloudOrLocal;
}
void FileLoader::increaseLoadSize(int size) {
Expects(size > _size);
_size = size;
}
void FileLoader::notifyAboutProgress() {
_updates.fire({});
}
@ -213,6 +211,13 @@ void FileLoader::localLoaded(
start();
return;
}
if (result.data.size() < _size) {
_localStatus = LocalStatus::NotFound;
if (checkForOpen()) {
startLoadingWithData(result.data);
}
return;
}
if (!imageData.isNull()) {
_imageFormat = imageFormat;
_imageData = imageData;
@ -228,13 +233,23 @@ void FileLoader::start() {
return;
}
if (!_filename.isEmpty() && _toCache == LoadToFileOnly && !_fileIsOpen) {
_fileIsOpen = _file.open(QIODevice::WriteOnly);
if (!_fileIsOpen) {
return cancel(true);
}
if (checkForOpen()) {
startLoading();
}
startLoading();
}
bool FileLoader::checkForOpen() {
if (_filename.isEmpty()
|| (_toCache != LoadToFileOnly)
|| _fileIsOpen) {
return true;
}
_fileIsOpen = _file.open(QIODevice::WriteOnly);
if (_fileIsOpen) {
return true;
}
cancel(true);
return false;
}
void FileLoader::loadLocal(const Storage::Cache::Key &key) {

View File

@ -80,9 +80,7 @@ public:
[[nodiscard]] virtual uint64 objId() const {
return 0;
}
[[nodiscard]] QByteArray imageFormat(
const QSize &shrinkBox = QSize()) const;
[[nodiscard]] QImage imageData(const QSize &shrinkBox = QSize()) const;
[[nodiscard]] QImage imageData(int progressiveSizeLimit = 0) const;
[[nodiscard]] QString fileName() const {
return _filename;
}
@ -94,6 +92,7 @@ public:
bool setFileName(const QString &filename); // set filename for loaders to cache
void permitLoadFromCloud();
void increaseLoadSize(int size);
void start();
void cancel();
@ -126,14 +125,18 @@ protected:
Loaded,
};
void readImage(const QSize &shrinkBox) const;
void readImage(int progressiveSizeLimit) const;
bool checkForOpen();
bool tryLoadLocal();
void loadLocal(const Storage::Cache::Key &key);
virtual Storage::Cache::Key cacheKey() const = 0;
virtual std::optional<MediaKey> fileLocationKey() const = 0;
virtual void cancelHook() = 0;
virtual void startLoading() = 0;
virtual void startLoadingWithData(const QByteArray &data) {
startLoading();
}
void cancel(bool failed);

View File

@ -114,7 +114,9 @@ int mtpFileLoader::takeNextRequestOffset() {
}
bool mtpFileLoader::feedPart(int offset, const QByteArray &bytes) {
const auto buffer = bytes::make_span(bytes);
const auto buffer = (_size > 0 && offset + bytes.size() > _size)
? bytes::make_span(bytes).subspan(0, _size - offset)
: bytes::make_span(bytes);
if (!writeResultPart(offset, buffer)) {
return false;
}
@ -155,6 +157,16 @@ void mtpFileLoader::startLoading() {
addToQueue();
}
void mtpFileLoader::startLoadingWithData(const QByteArray &data) {
const auto parts = data.size() / Storage::kDownloadPartSize;
const auto use = parts * Storage::kDownloadPartSize;
if (use > 0) {
_nextRequestOffset = use;
feedPart(0, QByteArray::fromRawData(data.data(), use));
}
startLoading();
}
void mtpFileLoader::cancelHook() {
cancelAllRequests();
}

View File

@ -48,6 +48,7 @@ private:
Storage::Cache::Key cacheKey() const override;
std::optional<MediaKey> fileLocationKey() const override;
void startLoading() override;
void startLoadingWithData(const QByteArray &data) override;
void cancelHook() override;
bool readyToRequest() const override;

View File

@ -614,6 +614,7 @@ struct ImageWithLocation {
QByteArray bytes;
QImage preloaded;
int bytesCount = 0;
int progressivePartSize = 0;
};
InMemoryKey inMemoryKey(const StorageFileLocation &location);

View File

@ -96,6 +96,21 @@ ImageWithLocation FromPhotoSize(
});
}
ImageWithLocation FromProgressiveSize(
not_null<Main::Session*> session,
const MTPPhotoSize &size,
int index) {
Expects(size.type() == mtpc_photoSizeProgressive);
const auto &data = size.c_photoSizeProgressive();
if (data.vsizes().v.size() <= index) {
return ImageWithLocation();
}
return ImageWithLocation{
.progressivePartSize = data.vsizes().v[index].v,
};
}
ImageWithLocation FromPhotoSize(
not_null<Main::Session*> session,
const MTPDdocument &document,
@ -133,7 +148,6 @@ ImageWithLocation FromPhotoSize(
.bytesCount = bytes.size(),
};
}, [&](const MTPDphotoSizeProgressive &data) {
// #TODO layer118
if (data.vsizes().v.isEmpty()) {
return ImageWithLocation();
}
@ -214,7 +228,6 @@ ImageWithLocation FromPhotoSize(
.bytesCount = bytes.size(),
};
}, [&](const MTPDphotoSizeProgressive &data) {
// #TODO layer118
if (data.vsizes().v.isEmpty()) {
return ImageWithLocation();
}

View File

@ -19,6 +19,10 @@ namespace Images {
not_null<Main::Session*> session,
const MTPDphoto &photo,
const MTPPhotoSize &size);
[[nodiscard]] ImageWithLocation FromProgressiveSize(
not_null<Main::Session*> session,
const MTPPhotoSize &size,
int index);
[[nodiscard]] ImageWithLocation FromPhotoSize(
not_null<Main::Session*> session,
const MTPDdocument &document,

@ -1 +1 @@
Subproject commit b221a088a701ab8af60feb8deed87313500af435
Subproject commit db23f73e451e3ae11551e7a444ec1a4087348e5c