diff --git a/Telegram/SourceFiles/calls/calls_call.cpp b/Telegram/SourceFiles/calls/calls_call.cpp index 4f82faa981..4ba508ecaa 100644 --- a/Telegram/SourceFiles/calls/calls_call.cpp +++ b/Telegram/SourceFiles/calls/calls_call.cpp @@ -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(); diff --git a/Telegram/SourceFiles/data/data_cloud_file.cpp b/Telegram/SourceFiles/data/data_cloud_file.cpp index 65f3ed6a68..1e793e95ba 100644 --- a/Telegram/SourceFiles/data/data_cloud_file.cpp +++ b/Telegram/SourceFiles/data/data_cloud_file.cpp @@ -160,6 +160,9 @@ void UpdateCloudFile( Fn restartLoader, Fn usePreloaded) { if (!data.location.valid()) { + if (data.progressivePartSize && !file.location.valid()) { + file.progressivePartSize = data.progressivePartSize; + } return; } @@ -208,11 +211,18 @@ void LoadCloudFile( Fn finalCheck, Fn done, Fn fail, - Fn progress) { + Fn 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 finalCheck, Fn done, Fn fail, - Fn progress) { + Fn 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( diff --git a/Telegram/SourceFiles/data/data_cloud_file.h b/Telegram/SourceFiles/data/data_cloud_file.h index 2a8740abc0..a1ccc7afe9 100644 --- a/Telegram/SourceFiles/data/data_cloud_file.h +++ b/Telegram/SourceFiles/data/data_cloud_file.h @@ -39,6 +39,7 @@ struct CloudFile final { ImageLocation location; std::unique_ptr loader; int byteSize = 0; + int progressivePartSize = 0; base::flags flags; }; @@ -105,7 +106,8 @@ void LoadCloudFile( Fn finalCheck, Fn done, Fn fail = nullptr, - Fn progress = nullptr); + Fn progress = nullptr, + int downloadFrontPartSize = 0); void LoadCloudFile( not_null session, diff --git a/Telegram/SourceFiles/data/data_photo.cpp b/Telegram/SourceFiles/data/data_photo.cpp index e58d433310..b748dadb89 100644 --- a/Telegram/SourceFiles/data/data_photo.cpp +++ b/Telegram/SourceFiles/data/data_photo.cpp @@ -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 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(index); + const auto validSize = static_cast(valid); + const auto existingSize = static_cast(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); } }); }; diff --git a/Telegram/SourceFiles/data/data_photo.h b/Telegram/SourceFiles/data/data_photo.h index 0f887e55c9..c7442551d8 100644 --- a/Telegram/SourceFiles/data/data_photo.h +++ b/Telegram/SourceFiles/data/data_photo.h @@ -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; diff --git a/Telegram/SourceFiles/data/data_photo_media.cpp b/Telegram/SourceFiles/data/data_photo_media.cpp index d636d5ac2d..1bfc160f24 100644 --- a/Telegram/SourceFiles/data/data_photo_media.cpp +++ b/Telegram/SourceFiles/data/data_photo_media.cpp @@ -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(std::move(image)); + _images[index] = PhotoImage{ + .data = std::make_unique(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 local) { _inlineThumbnail = std::make_unique(image->original()); } for (auto i = 0; i != kPhotoSizeCount; ++i) { - if (const auto image = local->_images[i].get()) { - _images[i] = std::make_unique(image->original()); + if (const auto image = local->_images[i].data.get()) { + _images[i] = PhotoImage{ + .data = std::make_unique(image->original()), + .goodFor = local->_images[i].goodFor + }; } } } diff --git a/Telegram/SourceFiles/data/data_photo_media.h b/Telegram/SourceFiles/data/data_photo_media.h index 4898b50289..42f52b8051 100644 --- a/Telegram/SourceFiles/data/data_photo_media.h +++ b/Telegram/SourceFiles/data/data_photo_media.h @@ -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 local); private: + struct PhotoImage { + std::unique_ptr 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 _owner; mutable std::unique_ptr _inlineThumbnail; - std::array, kPhotoSizeCount> _images; + std::array _images; QByteArray _videoBytes; }; diff --git a/Telegram/SourceFiles/data/data_session.cpp b/Telegram/SourceFiles/data/data_session.cpp index ced7d72e2c..8811b7b4c9 100644 --- a/Telegram/SourceFiles/data/data_session.cpp +++ b/Telegram/SourceFiles/data/data_session.cpp @@ -2262,6 +2262,21 @@ void Session::photoApplyFields( not_null 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) diff --git a/Telegram/SourceFiles/export/data/export_data_types.cpp b/Telegram/SourceFiles/export/data/export_data_types.cpp index 9e5409de6b..135b2a84fe 100644 --- a/Telegram/SourceFiles/export/data/export_data_types.cpp +++ b/Telegram/SourceFiles/export/data/export_data_types.cpp @@ -265,8 +265,11 @@ Image ParseMaxImage( result.file.content = data.vbytes().v; result.file.size = result.file.content.size(); } else if constexpr (MTPDphotoSizeProgressive::Is()) { + 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()) { + 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; diff --git a/Telegram/SourceFiles/storage/file_download.cpp b/Telegram/SourceFiles/storage/file_download.cpp index a35a790f72..53e070193c 100644 --- a/Telegram/SourceFiles/storage/file_download.cpp +++ b/Telegram/SourceFiles/storage/file_download.cpp @@ -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) { diff --git a/Telegram/SourceFiles/storage/file_download.h b/Telegram/SourceFiles/storage/file_download.h index 7615bcde49..9a2c929de2 100644 --- a/Telegram/SourceFiles/storage/file_download.h +++ b/Telegram/SourceFiles/storage/file_download.h @@ -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 fileLocationKey() const = 0; virtual void cancelHook() = 0; virtual void startLoading() = 0; + virtual void startLoadingWithData(const QByteArray &data) { + startLoading(); + } void cancel(bool failed); diff --git a/Telegram/SourceFiles/storage/file_download_mtproto.cpp b/Telegram/SourceFiles/storage/file_download_mtproto.cpp index 02098d83b4..1ec361a1d8 100644 --- a/Telegram/SourceFiles/storage/file_download_mtproto.cpp +++ b/Telegram/SourceFiles/storage/file_download_mtproto.cpp @@ -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(); } diff --git a/Telegram/SourceFiles/storage/file_download_mtproto.h b/Telegram/SourceFiles/storage/file_download_mtproto.h index a66d977855..b29fda1f1b 100644 --- a/Telegram/SourceFiles/storage/file_download_mtproto.h +++ b/Telegram/SourceFiles/storage/file_download_mtproto.h @@ -48,6 +48,7 @@ private: Storage::Cache::Key cacheKey() const override; std::optional fileLocationKey() const override; void startLoading() override; + void startLoadingWithData(const QByteArray &data) override; void cancelHook() override; bool readyToRequest() const override; diff --git a/Telegram/SourceFiles/ui/image/image_location.h b/Telegram/SourceFiles/ui/image/image_location.h index a477e876e6..bded985526 100644 --- a/Telegram/SourceFiles/ui/image/image_location.h +++ b/Telegram/SourceFiles/ui/image/image_location.h @@ -614,6 +614,7 @@ struct ImageWithLocation { QByteArray bytes; QImage preloaded; int bytesCount = 0; + int progressivePartSize = 0; }; InMemoryKey inMemoryKey(const StorageFileLocation &location); diff --git a/Telegram/SourceFiles/ui/image/image_location_factory.cpp b/Telegram/SourceFiles/ui/image/image_location_factory.cpp index 72704c523d..4de02e6356 100644 --- a/Telegram/SourceFiles/ui/image/image_location_factory.cpp +++ b/Telegram/SourceFiles/ui/image/image_location_factory.cpp @@ -96,6 +96,21 @@ ImageWithLocation FromPhotoSize( }); } +ImageWithLocation FromProgressiveSize( + not_null 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 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(); } diff --git a/Telegram/SourceFiles/ui/image/image_location_factory.h b/Telegram/SourceFiles/ui/image/image_location_factory.h index 229a522e5c..9e27cd4dc6 100644 --- a/Telegram/SourceFiles/ui/image/image_location_factory.h +++ b/Telegram/SourceFiles/ui/image/image_location_factory.h @@ -19,6 +19,10 @@ namespace Images { not_null session, const MTPDphoto &photo, const MTPPhotoSize &size); +[[nodiscard]] ImageWithLocation FromProgressiveSize( + not_null session, + const MTPPhotoSize &size, + int index); [[nodiscard]] ImageWithLocation FromPhotoSize( not_null session, const MTPDdocument &document, diff --git a/Telegram/ThirdParty/tgcalls b/Telegram/ThirdParty/tgcalls index b221a088a7..db23f73e45 160000 --- a/Telegram/ThirdParty/tgcalls +++ b/Telegram/ThirdParty/tgcalls @@ -1 +1 @@ -Subproject commit b221a088a701ab8af60feb8deed87313500af435 +Subproject commit db23f73e451e3ae11551e7a444ec1a4087348e5c