Implement progressive jpeg loading and sending.

This commit is contained in:
John Preston 2020-08-26 17:28:56 +04:00
parent 0888901d79
commit 437c9320cd
22 changed files with 201 additions and 96 deletions

View file

@ -214,14 +214,14 @@ void LoadCloudFile(
Fn<void()> progress, Fn<void()> progress,
int downloadFrontPartSize = 0) { int downloadFrontPartSize = 0) {
const auto loadSize = downloadFrontPartSize const auto loadSize = downloadFrontPartSize
? downloadFrontPartSize ? std::min(downloadFrontPartSize, file.byteSize)
: file.byteSize; : file.byteSize;
if (file.loader) { if (file.loader) {
if (fromCloud == LoadFromCloudOrLocal) { if (fromCloud == LoadFromCloudOrLocal) {
file.loader->permitLoadFromCloud(); file.loader->permitLoadFromCloud();
} }
if (file.loader->fullSize() < loadSize) { if (file.loader->loadSize() < loadSize) {
file.loader->increaseLoadSize(loadSize); file.loader->increaseLoadSize(loadSize, autoLoading);
} }
return; return;
} else if ((file.flags & CloudFile::Flag::Failed) } else if ((file.flags & CloudFile::Flag::Failed)
@ -236,6 +236,7 @@ void LoadCloudFile(
origin, origin,
QString(), QString(),
loadSize, loadSize,
file.byteSize,
UnknownFileLocation, UnknownFileLocation,
LoadToCacheAsWell, LoadToCacheAsWell,
fromCloud, fromCloud,

View file

@ -1013,6 +1013,7 @@ void DocumentData::save(
&session(), &session(),
_urlLocation, _urlLocation,
size, size,
size,
fromCloud, fromCloud,
autoLoading, autoLoading,
cacheTag()); cacheTag());
@ -1039,6 +1040,7 @@ void DocumentData::save(
locationType(), locationType(),
toFile, toFile,
size, size,
size,
(saveToCache() ? LoadToCacheAsWell : LoadToFileOnly), (saveToCache() ? LoadToCacheAsWell : LoadToFileOnly),
fromCloud, fromCloud,
autoLoading, autoLoading,

View file

@ -104,7 +104,7 @@ bool PhotoData::loading(PhotoSize size) const {
} else if (valid == existing) { } else if (valid == existing) {
return true; return true;
} }
return (_images[valid].loader->fullSize() return (_images[valid].loader->loadSize()
>= _images[existing].progressivePartSize); >= _images[existing].progressivePartSize);
} }
@ -113,6 +113,10 @@ bool PhotoData::failed(PhotoSize size) const {
return (flags & Data::CloudFile::Flag::Failed); return (flags & Data::CloudFile::Flag::Failed);
} }
void PhotoData::clearFailed(PhotoSize size) {
_images[validSizeIndex(size)].flags &= ~Data::CloudFile::Flag::Failed;
}
const ImageLocation &PhotoData::location(PhotoSize size) const { const ImageLocation &PhotoData::location(PhotoSize size) const {
return _images[validSizeIndex(size)].location; return _images[validSizeIndex(size)].location;
} }
@ -144,10 +148,11 @@ int PhotoData::imageByteSize(PhotoSize size) const {
bool PhotoData::displayLoading() const { bool PhotoData::displayLoading() const {
const auto index = PhotoSizeIndex(PhotoSize::Large); const auto index = PhotoSizeIndex(PhotoSize::Large);
return _images[index].loader if (const auto loader = _images[index].loader.get()) {
? (!_images[index].loader->loadingLocal() return !loader->finished()
|| !_images[index].loader->autoLoading()) && (!loader->loadingLocal() || !loader->autoLoading());
: (uploading() && !waitingForAlbum()); }
return (uploading() && !waitingForAlbum());
} }
void PhotoData::cancel() { void PhotoData::cancel() {
@ -262,7 +267,6 @@ void PhotoData::load(
// Could've changed, if the requested size didn't have a location. // Could've changed, if the requested size didn't have a location.
const auto validSize = static_cast<PhotoSize>(valid); const auto validSize = static_cast<PhotoSize>(valid);
const auto existingSize = static_cast<PhotoSize>(existing);
const auto finalCheck = [=] { const auto finalCheck = [=] {
if (const auto active = activeMediaView()) { if (const auto active = activeMediaView()) {
return !active->image(size); return !active->image(size);
@ -270,10 +274,25 @@ void PhotoData::load(
return true; return true;
}; };
const auto done = [=](QImage result) { const auto done = [=](QImage result) {
if (const auto active = activeMediaView()) { Expects(_images[valid].loader != nullptr);
active->set(validSize, existingSize, std::move(result));
// Find out what progressive photo size have we loaded exactly.
auto goodFor = validSize;
const auto loadSize = _images[valid].loader->loadSize();
if (valid > 0 && _images[valid].byteSize > loadSize) {
for (auto i = valid; i != 0;) {
--i;
const auto required = _images[i].progressivePartSize;
if (required > 0 && required <= loadSize) {
goodFor = static_cast<PhotoSize>(i);
break;
}
}
} }
if (validSize == PhotoSize::Large) { if (const auto active = activeMediaView()) {
active->set(validSize, goodFor, std::move(result));
}
if (validSize == PhotoSize::Large && goodFor == validSize) {
_owner->photoLoadDone(this); _owner->photoLoadDone(this);
} }
}; };
@ -453,6 +472,7 @@ void PhotoSaveClickHandler::onClickImpl() const {
if (!data->date) { if (!data->date) {
return; return;
} else { } else {
data->clearFailed(PhotoSize::Large);
data->load(context()); data->load(context());
} }
} }

View file

@ -111,6 +111,7 @@ public:
[[nodiscard]] bool hasExact(Data::PhotoSize size) const; [[nodiscard]] bool hasExact(Data::PhotoSize size) const;
[[nodiscard]] bool loading(Data::PhotoSize size) const; [[nodiscard]] bool loading(Data::PhotoSize size) const;
[[nodiscard]] bool failed(Data::PhotoSize size) const; [[nodiscard]] bool failed(Data::PhotoSize size) const;
void clearFailed(Data::PhotoSize size);
void load( void load(
Data::PhotoSize size, Data::PhotoSize size,
Data::FileOrigin origin, Data::FileOrigin origin,

View file

@ -51,11 +51,15 @@ Image *PhotoMedia::thumbnailInline() const {
Image *PhotoMedia::image(PhotoSize size) const { Image *PhotoMedia::image(PhotoSize size) const {
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()) {
return (original.goodFor >= size) ? image : nullptr; if (original.goodFor >= size) {
return image;
}
} }
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()) {
return (valid.goodFor >= size) ? image : nullptr; if (valid.goodFor >= size) {
return image;
}
} }
return nullptr; return nullptr;
} }

View file

@ -2147,7 +2147,10 @@ not_null<PhotoData*> Session::processPhoto(
const auto i = find(levels); const auto i = find(levels);
return (i == thumbs.end()) return (i == thumbs.end())
? ImageWithLocation() ? ImageWithLocation()
: Images::FromImageInMemory(i->second, "JPG"); : Images::FromImageInMemory(
i->second.image,
"JPG",
i->second.bytes);
}; };
const auto small = image(SmallLevels); const auto small = image(SmallLevels);
const auto thumbnail = image(ThumbnailLevels); const auto thumbnail = image(ThumbnailLevels);
@ -2271,7 +2274,7 @@ void Session::photoApplyFields(
return 0; return 0;
}); });
}; };
const auto found = ranges::max_element(sizes, std::greater<>(), area); const auto found = ranges::max_element(sizes, std::less<>(), area);
return (found == sizes.end() return (found == sizes.end()
|| found->type() != mtpc_photoSizeProgressive) || found->type() != mtpc_photoSizeProgressive)
? sizes.end() ? sizes.end()

View file

@ -287,7 +287,11 @@ using PollId = uint64;
using WallPaperId = uint64; using WallPaperId = uint64;
constexpr auto CancelledWebPageId = WebPageId(0xFFFFFFFFFFFFFFFFULL); constexpr auto CancelledWebPageId = WebPageId(0xFFFFFFFFFFFFFFFFULL);
using PreparedPhotoThumbs = base::flat_map<char, QImage>; struct PreparedPhotoThumb {
QImage image;
QByteArray bytes;
};
using PreparedPhotoThumbs = base::flat_map<char, PreparedPhotoThumb>;
// [0] == -1 -- counting, [0] == -2 -- could not count // [0] == -1 -- counting, [0] == -2 -- could not count
using VoiceWaveform = QVector<signed char>; using VoiceWaveform = QVector<signed char>;

View file

@ -2137,7 +2137,7 @@ void OverlayWidget::displayDocument(
_themeCloudData = cloud; _themeCloudData = cloud;
_radial.stop(); _radial.stop();
_touchbarDisplay.fire(TouchBarItemType::None); _touchbarDisplay.fire(TouchBarItemType::None);
refreshMediaViewer(); refreshMediaViewer();
if (_document) { if (_document) {
@ -4161,6 +4161,9 @@ void OverlayWidget::setVisibleHook(bool visible) {
_userPhotosData = std::nullopt; _userPhotosData = std::nullopt;
_collage = nullptr; _collage = nullptr;
_collageData = std::nullopt; _collageData = std::nullopt;
assignMediaPointer(nullptr);
_preloadPhotos.clear();
_preloadDocuments.clear();
if (_menu) _menu->hideMenu(true); if (_menu) _menu->hideMenu(true);
_controlsHideTimer.stop(); _controlsHideTimer.stop();
_controlsState = ControlsShown; _controlsState = ControlsShown;

View file

@ -1751,6 +1751,7 @@ void FormController::loadFile(File &file) {
SecureFileLocation, SecureFileLocation,
QString(), QString(),
file.size, file.size,
file.size,
LoadToCacheAsWell, LoadToCacheAsWell,
LoadFromCloudOrLocal, LoadFromCloudOrLocal,
false, false,

View file

@ -32,7 +32,8 @@ public:
not_null<Main::Session*> session, not_null<Main::Session*> session,
const QByteArray &data, const QByteArray &data,
const QString &toFile, const QString &toFile,
int32 size, int loadSize,
int fullSize,
LocationType locationType, LocationType locationType,
LoadToCacheSetting toCache, LoadToCacheSetting toCache,
LoadFromCloudSetting fromCloud, LoadFromCloudSetting fromCloud,
@ -53,7 +54,8 @@ FromMemoryLoader::FromMemoryLoader(
not_null<Main::Session*> session, not_null<Main::Session*> session,
const QByteArray &data, const QByteArray &data,
const QString &toFile, const QString &toFile,
int32 size, int loadSize,
int fullSize,
LocationType locationType, LocationType locationType,
LoadToCacheSetting toCache, LoadToCacheSetting toCache,
LoadFromCloudSetting fromCloud, LoadFromCloudSetting fromCloud,
@ -62,7 +64,8 @@ FromMemoryLoader::FromMemoryLoader(
) : FileLoader( ) : FileLoader(
session, session,
toFile, toFile,
size, loadSize,
fullSize,
locationType, locationType,
toCache, toCache,
fromCloud, fromCloud,
@ -91,7 +94,8 @@ void FromMemoryLoader::startLoading() {
FileLoader::FileLoader( FileLoader::FileLoader(
not_null<Main::Session*> session, not_null<Main::Session*> session,
const QString &toFile, const QString &toFile,
int32 size, int loadSize,
int fullSize,
LocationType locationType, LocationType locationType,
LoadToCacheSetting toCache, LoadToCacheSetting toCache,
LoadFromCloudSetting fromCloud, LoadFromCloudSetting fromCloud,
@ -104,9 +108,11 @@ FileLoader::FileLoader(
, _file(_filename) , _file(_filename)
, _toCache(toCache) , _toCache(toCache)
, _fromCloud(fromCloud) , _fromCloud(fromCloud)
, _size(size) , _loadSize(loadSize)
, _fullSize(fullSize)
, _locationType(locationType) { , _locationType(locationType) {
Expects(!_filename.isEmpty() || (_size <= Storage::kMaxFileInMemory)); Expects(_loadSize <= _fullSize);
Expects(!_filename.isEmpty() || (_fullSize <= Storage::kMaxFileInMemory));
} }
FileLoader::~FileLoader() { FileLoader::~FileLoader() {
@ -169,13 +175,11 @@ Data::FileOrigin FileLoader::fileOrigin() const {
} }
float64 FileLoader::currentProgress() const { float64 FileLoader::currentProgress() const {
if (_finished) return 1.; return _finished
if (!fullSize()) return 0.; ? 1.
return snap(float64(currentOffset()) / fullSize(), 0., 1.); : !_loadSize
} ? 0.
: std::clamp(float64(currentOffset()) / _loadSize, 0., 1.);
int FileLoader::fullSize() const {
return _size;
} }
bool FileLoader::setFileName(const QString &fileName) { bool FileLoader::setFileName(const QString &fileName) {
@ -191,10 +195,12 @@ void FileLoader::permitLoadFromCloud() {
_fromCloud = LoadFromCloudOrLocal; _fromCloud = LoadFromCloudOrLocal;
} }
void FileLoader::increaseLoadSize(int size) { void FileLoader::increaseLoadSize(int size, bool autoLoading) {
Expects(size > _size); Expects(size > _loadSize);
Expects(size <= _fullSize);
_size = size; _loadSize = size;
_autoLoading = autoLoading;
} }
void FileLoader::notifyAboutProgress() { void FileLoader::notifyAboutProgress() {
@ -211,10 +217,12 @@ void FileLoader::localLoaded(
start(); start();
return; return;
} }
if (result.data.size() < _size) { const auto partial = result.data.startsWith("partial:");
constexpr auto kPrefix = 8;
if (partial && result.data.size() < _loadSize + kPrefix) {
_localStatus = LocalStatus::NotFound; _localStatus = LocalStatus::NotFound;
if (checkForOpen()) { if (checkForOpen()) {
startLoadingWithData(result.data); startLoadingWithPartial(result.data);
} }
return; return;
} }
@ -222,7 +230,11 @@ void FileLoader::localLoaded(
_imageFormat = imageFormat; _imageFormat = imageFormat;
_imageData = imageData; _imageData = imageData;
} }
finishWithBytes(result.data); finishWithBytes(partial
? QByteArray::fromRawData(
result.data.data() + kPrefix,
result.data.size() - kPrefix)
: result.data);
} }
void FileLoader::start() { void FileLoader::start() {
@ -272,7 +284,7 @@ void FileLoader::loadLocal(const Storage::Cache::Key &key) {
}; };
_session->data().cache().get(key, [=, callback = std::move(done)]( _session->data().cache().get(key, [=, callback = std::move(done)](
QByteArray &&value) mutable { QByteArray &&value) mutable {
if (readImage) { if (readImage && !value.startsWith("partial:")) {
crl::async([ crl::async([
value = std::move(value), value = std::move(value),
done = std::move(callback) done = std::move(callback)
@ -452,7 +464,9 @@ bool FileLoader::finalizeResult() {
_session->data().cache().put( _session->data().cache().put(
cacheKey(), cacheKey(),
Storage::Cache::Database::TaggedValue( Storage::Cache::Database::TaggedValue(
base::duplicate(_data), base::duplicate((!_fullSize || _data.size() == _fullSize)
? _data
: ("partial:" + _data)),
_cacheTag)); _cacheTag));
} }
} }
@ -466,7 +480,8 @@ std::unique_ptr<FileLoader> CreateFileLoader(
const DownloadLocation &location, const DownloadLocation &location,
Data::FileOrigin origin, Data::FileOrigin origin,
const QString &toFile, const QString &toFile,
int size, int loadSize,
int fullSize,
LocationType locationType, LocationType locationType,
LoadToCacheSetting toCache, LoadToCacheSetting toCache,
LoadFromCloudSetting fromCloud, LoadFromCloudSetting fromCloud,
@ -480,7 +495,8 @@ std::unique_ptr<FileLoader> CreateFileLoader(
origin, origin,
locationType, locationType,
toFile, toFile,
size, loadSize,
fullSize,
toCache, toCache,
fromCloud, fromCloud,
autoLoading, autoLoading,
@ -489,7 +505,8 @@ std::unique_ptr<FileLoader> CreateFileLoader(
result = std::make_unique<mtpFileLoader>( result = std::make_unique<mtpFileLoader>(
session, session,
data, data,
size, loadSize,
fullSize,
fromCloud, fromCloud,
autoLoading, autoLoading,
cacheTag); cacheTag);
@ -497,7 +514,8 @@ std::unique_ptr<FileLoader> CreateFileLoader(
result = std::make_unique<mtpFileLoader>( result = std::make_unique<mtpFileLoader>(
session, session,
data, data,
size, loadSize,
fullSize,
fromCloud, fromCloud,
autoLoading, autoLoading,
cacheTag); cacheTag);
@ -514,7 +532,8 @@ std::unique_ptr<FileLoader> CreateFileLoader(
session, session,
data.bytes, data.bytes,
toFile, toFile,
size, loadSize,
fullSize,
locationType, locationType,
toCache, toCache,
LoadFromCloudOrLocal, LoadFromCloudOrLocal,

View file

@ -57,7 +57,8 @@ public:
FileLoader( FileLoader(
not_null<Main::Session*> session, not_null<Main::Session*> session,
const QString &toFile, const QString &toFile,
int32 size, int loadSize,
int fullSize,
LocationType locationType, LocationType locationType,
LoadToCacheSetting toCache, LoadToCacheSetting toCache,
LoadFromCloudSetting fromCloud, LoadFromCloudSetting fromCloud,
@ -88,11 +89,16 @@ public:
[[nodiscard]] virtual Data::FileOrigin fileOrigin() const; [[nodiscard]] virtual Data::FileOrigin fileOrigin() const;
[[nodiscard]] float64 currentProgress() const; [[nodiscard]] float64 currentProgress() const;
[[nodiscard]] virtual int currentOffset() const; [[nodiscard]] virtual int currentOffset() const;
[[nodiscard]] int fullSize() const; [[nodiscard]] int fullSize() const {
return _fullSize;
}
[[nodiscard]] int loadSize() const {
return _loadSize;
}
bool setFileName(const QString &filename); // set filename for loaders to cache bool setFileName(const QString &filename); // set filename for loaders to cache
void permitLoadFromCloud(); void permitLoadFromCloud();
void increaseLoadSize(int size); void increaseLoadSize(int size, bool autoLoading);
void start(); void start();
void cancel(); void cancel();
@ -134,7 +140,7 @@ protected:
virtual std::optional<MediaKey> fileLocationKey() const = 0; virtual std::optional<MediaKey> fileLocationKey() const = 0;
virtual void cancelHook() = 0; virtual void cancelHook() = 0;
virtual void startLoading() = 0; virtual void startLoading() = 0;
virtual void startLoadingWithData(const QByteArray &data) { virtual void startLoadingWithPartial(const QByteArray &data) {
startLoading(); startLoading();
} }
@ -163,7 +169,8 @@ protected:
QByteArray _data; QByteArray _data;
int _size = 0; int _loadSize = 0;
int _fullSize = 0;
int _skippedBytes = 0; int _skippedBytes = 0;
LocationType _locationType = LocationType(); LocationType _locationType = LocationType();
@ -181,7 +188,8 @@ protected:
const DownloadLocation &location, const DownloadLocation &location,
Data::FileOrigin origin, Data::FileOrigin origin,
const QString &toFile, const QString &toFile,
int size, int loadSize,
int fullSize,
LocationType locationType, LocationType locationType,
LoadToCacheSetting toCache, LoadToCacheSetting toCache,
LoadFromCloudSetting fromCloud, LoadFromCloudSetting fromCloud,

View file

@ -23,7 +23,8 @@ mtpFileLoader::mtpFileLoader(
Data::FileOrigin origin, Data::FileOrigin origin,
LocationType type, LocationType type,
const QString &to, const QString &to,
int32 size, int loadSize,
int fullSize,
LoadToCacheSetting toCache, LoadToCacheSetting toCache,
LoadFromCloudSetting fromCloud, LoadFromCloudSetting fromCloud,
bool autoLoading, bool autoLoading,
@ -31,7 +32,8 @@ mtpFileLoader::mtpFileLoader(
: FileLoader( : FileLoader(
session, session,
to, to,
size, loadSize,
fullSize,
type, type,
toCache, toCache,
fromCloud, fromCloud,
@ -43,14 +45,16 @@ mtpFileLoader::mtpFileLoader(
mtpFileLoader::mtpFileLoader( mtpFileLoader::mtpFileLoader(
not_null<Main::Session*> session, not_null<Main::Session*> session,
const WebFileLocation &location, const WebFileLocation &location,
int32 size, int loadSize,
int fullSize,
LoadFromCloudSetting fromCloud, LoadFromCloudSetting fromCloud,
bool autoLoading, bool autoLoading,
uint8 cacheTag) uint8 cacheTag)
: FileLoader( : FileLoader(
session, session,
QString(), QString(),
size, loadSize,
fullSize,
UnknownFileLocation, UnknownFileLocation,
LoadToCacheAsWell, LoadToCacheAsWell,
fromCloud, fromCloud,
@ -65,14 +69,16 @@ mtpFileLoader::mtpFileLoader(
mtpFileLoader::mtpFileLoader( mtpFileLoader::mtpFileLoader(
not_null<Main::Session*> session, not_null<Main::Session*> session,
const GeoPointLocation &location, const GeoPointLocation &location,
int32 size, int loadSize,
int fullSize,
LoadFromCloudSetting fromCloud, LoadFromCloudSetting fromCloud,
bool autoLoading, bool autoLoading,
uint8 cacheTag) uint8 cacheTag)
: FileLoader( : FileLoader(
session, session,
QString(), QString(),
size, loadSize,
fullSize,
UnknownFileLocation, UnknownFileLocation,
LoadToCacheAsWell, LoadToCacheAsWell,
fromCloud, fromCloud,
@ -101,8 +107,8 @@ uint64 mtpFileLoader::objId() const {
bool mtpFileLoader::readyToRequest() const { bool mtpFileLoader::readyToRequest() const {
return !_finished return !_finished
&& !_lastComplete && !_lastComplete
&& (_size != 0 || !haveSentRequests()) && (_fullSize != 0 || !haveSentRequests())
&& (!_size || _nextRequestOffset < _size); && (!_fullSize || _nextRequestOffset < _loadSize);
} }
int mtpFileLoader::takeNextRequestOffset() { int mtpFileLoader::takeNextRequestOffset() {
@ -114,9 +120,7 @@ int mtpFileLoader::takeNextRequestOffset() {
} }
bool mtpFileLoader::feedPart(int offset, const QByteArray &bytes) { bool mtpFileLoader::feedPart(int offset, const QByteArray &bytes) {
const auto buffer = (_size > 0 && offset + bytes.size() > _size) const auto buffer = bytes::make_span(bytes);
? bytes::make_span(bytes).subspan(0, _size - offset)
: bytes::make_span(bytes);
if (!writeResultPart(offset, buffer)) { if (!writeResultPart(offset, buffer)) {
return false; return false;
} }
@ -124,7 +128,7 @@ bool mtpFileLoader::feedPart(int offset, const QByteArray &bytes) {
_lastComplete = true; _lastComplete = true;
} }
const auto finished = !haveSentRequests() const auto finished = !haveSentRequests()
&& (_lastComplete || (_size && _nextRequestOffset >= _size)); && (_lastComplete || (_fullSize && _nextRequestOffset >= _loadSize));
if (finished) { if (finished) {
removeFromQueue(); removeFromQueue();
if (!finalizeResult()) { if (!finalizeResult()) {
@ -141,13 +145,13 @@ void mtpFileLoader::cancelOnFail() {
} }
bool mtpFileLoader::setWebFileSizeHook(int size) { bool mtpFileLoader::setWebFileSizeHook(int size) {
if (!_size || _size == size) { if (!_fullSize || _fullSize == size) {
_size = size; _fullSize = _loadSize = size;
return true; return true;
} }
LOG(("MTP Error: " LOG(("MTP Error: "
"Bad size provided by bot for webDocument: %1, real: %2" "Bad size provided by bot for webDocument: %1, real: %2"
).arg(_size ).arg(_fullSize
).arg(size)); ).arg(size));
cancel(true); cancel(true);
return false; return false;
@ -157,12 +161,15 @@ void mtpFileLoader::startLoading() {
addToQueue(); addToQueue();
} }
void mtpFileLoader::startLoadingWithData(const QByteArray &data) { void mtpFileLoader::startLoadingWithPartial(const QByteArray &data) {
const auto parts = data.size() / Storage::kDownloadPartSize; Expects(data.startsWith("partial:"));
constexpr auto kPrefix = 8;
const auto parts = (data.size() - kPrefix) / Storage::kDownloadPartSize;
const auto use = parts * Storage::kDownloadPartSize; const auto use = parts * Storage::kDownloadPartSize;
if (use > 0) { if (use > 0) {
_nextRequestOffset = use; _nextRequestOffset = use;
feedPart(0, QByteArray::fromRawData(data.data(), use)); feedPart(0, QByteArray::fromRawData(data.data() + kPrefix, use));
} }
startLoading(); startLoading();
} }

View file

@ -20,7 +20,8 @@ public:
Data::FileOrigin origin, Data::FileOrigin origin,
LocationType type, LocationType type,
const QString &toFile, const QString &toFile,
int32 size, int loadSize,
int fullSize,
LoadToCacheSetting toCache, LoadToCacheSetting toCache,
LoadFromCloudSetting fromCloud, LoadFromCloudSetting fromCloud,
bool autoLoading, bool autoLoading,
@ -28,14 +29,16 @@ public:
mtpFileLoader( mtpFileLoader(
not_null<Main::Session*> session, not_null<Main::Session*> session,
const WebFileLocation &location, const WebFileLocation &location,
int32 size, int loadSize,
int fullSize,
LoadFromCloudSetting fromCloud, LoadFromCloudSetting fromCloud,
bool autoLoading, bool autoLoading,
uint8 cacheTag); uint8 cacheTag);
mtpFileLoader( mtpFileLoader(
not_null<Main::Session*> session, not_null<Main::Session*> session,
const GeoPointLocation &location, const GeoPointLocation &location,
int32 size, int loadSize,
int fullSize,
LoadFromCloudSetting fromCloud, LoadFromCloudSetting fromCloud,
bool autoLoading, bool autoLoading,
uint8 cacheTag); uint8 cacheTag);
@ -48,7 +51,7 @@ private:
Storage::Cache::Key cacheKey() const override; Storage::Cache::Key cacheKey() const override;
std::optional<MediaKey> fileLocationKey() const override; std::optional<MediaKey> fileLocationKey() const override;
void startLoading() override; void startLoading() override;
void startLoadingWithData(const QByteArray &data) override; void startLoadingWithPartial(const QByteArray &data) override;
void cancelHook() override; void cancelHook() override;
bool readyToRequest() const override; bool readyToRequest() const override;

View file

@ -446,6 +446,7 @@ webFileLoader::webFileLoader(
session, session,
QString(), QString(),
0, 0,
0,
UnknownFileLocation, UnknownFileLocation,
LoadToCacheAsWell, LoadToCacheAsWell,
fromCloud, fromCloud,
@ -489,7 +490,7 @@ int webFileLoader::currentOffset() const {
} }
void webFileLoader::loadProgress(qint64 ready, qint64 total) { void webFileLoader::loadProgress(qint64 ready, qint64 total) {
_size = total; _fullSize = _loadSize = total;
_ready = ready; _ready = ready;
notifyAboutProgress(); notifyAboutProgress();
} }

View file

@ -311,8 +311,9 @@ void Uploader::uploadMedia(
: session().data().processDocument( : session().data().processDocument(
media.document, media.document,
Images::FromImageInMemory( Images::FromImageInMemory(
media.photoThumbs.front().second, media.photoThumbs.front().second.image,
"JPG")); "JPG",
media.photoThumbs.front().second.bytes));
if (!media.data.isEmpty()) { if (!media.data.isEmpty()) {
document->setDataAndCache(media.data); document->setDataAndCache(media.data);
if (media.type == SendMediaType::ThemeFile) { if (media.type == SendMediaType::ThemeFile) {
@ -345,7 +346,8 @@ void Uploader::upload(
file->document, file->document,
Images::FromImageInMemory( Images::FromImageInMemory(
file->thumb, file->thumb,
ThumbnailFormat(file->filemime))); ThumbnailFormat(file->filemime),
file->thumbbytes));
document->uploadingData = std::make_unique<Data::UploadState>( document->uploadingData = std::make_unique<Data::UploadState>(
document->size); document->size);
if (const auto active = document->activeMediaView()) { if (const auto active = document->activeMediaView()) {

View file

@ -32,6 +32,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "app.h" #include "app.h"
#include <QtCore/QBuffer> #include <QtCore/QBuffer>
#include <QtGui/QImageWriter>
namespace { namespace {
@ -202,17 +203,23 @@ SendMediaReady PreparePeerPhoto(MTP::DcId dcId, PeerId peerId, QImage &&image) {
Qt::KeepAspectRatio, Qt::KeepAspectRatio,
Qt::SmoothTransformation); Qt::SmoothTransformation);
}; };
const auto push = [&](const char *type, QImage &&image) { const auto push = [&](
const char *type,
QImage &&image,
QByteArray bytes = QByteArray()) {
photoSizes.push_back(MTP_photoSize( photoSizes.push_back(MTP_photoSize(
MTP_string(type), MTP_string(type),
MTP_fileLocationToBeDeprecated(MTP_long(0), MTP_int(0)), MTP_fileLocationToBeDeprecated(MTP_long(0), MTP_int(0)),
MTP_int(image.width()), MTP_int(image.width()),
MTP_int(image.height()), MTP_int(0))); MTP_int(image.height()), MTP_int(0)));
photoThumbs.emplace(type[0], std::move(image)); photoThumbs.emplace(type[0], PreparedPhotoThumb{
.image = std::move(image),
.bytes = std::move(bytes)
});
}; };
push("a", scaled(160)); push("a", scaled(160));
push("b", scaled(320)); push("b", scaled(320));
push("c", std::move(image)); push("c", std::move(image), jpeg);
const auto id = rand_value<PhotoId>(); const auto id = rand_value<PhotoId>();
const auto photo = MTP_photo( const auto photo = MTP_photo(
@ -467,6 +474,7 @@ void FileLoadResult::setFileData(const QByteArray &filedata) {
void FileLoadResult::setThumbData(const QByteArray &thumbdata) { void FileLoadResult::setThumbData(const QByteArray &thumbdata) {
if (!thumbdata.isEmpty()) { if (!thumbdata.isEmpty()) {
thumbbytes = thumbdata;
int32 size = thumbdata.size(); int32 size = thumbdata.size();
for (int32 i = 0, part = 0; i < size; i += kPhotoUploadPartSize, ++part) { for (int32 i = 0, part = 0; i < size; i += kPhotoUploadPartSize, ++part) {
thumbparts.insert(part, thumbdata.mid(i, kPhotoUploadPartSize)); thumbparts.insert(part, thumbdata.mid(i, kPhotoUploadPartSize));
@ -864,17 +872,22 @@ void FileLoadTask::process() {
attributes.push_back(MTP_documentAttributeAnimated()); attributes.push_back(MTP_documentAttributeAnimated());
} else if (_type != SendMediaType::File) { } else if (_type != SendMediaType::File) {
auto medium = (w > 320 || h > 320) ? fullimage.scaled(320, 320, Qt::KeepAspectRatio, Qt::SmoothTransformation) : fullimage; auto medium = (w > 320 || h > 320) ? fullimage.scaled(320, 320, Qt::KeepAspectRatio, Qt::SmoothTransformation) : fullimage;
photoThumbs.emplace('m', medium);
photoSizes.push_back(MTP_photoSize(MTP_string("m"), MTP_fileLocationToBeDeprecated(MTP_long(0), MTP_int(0)), MTP_int(medium.width()), MTP_int(medium.height()), MTP_int(0)));
auto full = (w > 1280 || h > 1280) ? fullimage.scaled(1280, 1280, Qt::KeepAspectRatio, Qt::SmoothTransformation) : fullimage; auto full = (w > 1280 || h > 1280) ? fullimage.scaled(1280, 1280, Qt::KeepAspectRatio, Qt::SmoothTransformation) : fullimage;
photoThumbs.emplace('y', full);
photoSizes.push_back(MTP_photoSize(MTP_string("y"), MTP_fileLocationToBeDeprecated(MTP_long(0), MTP_int(0)), MTP_int(full.width()), MTP_int(full.height()), MTP_int(0)));
{ {
QBuffer buffer(&filedata); QBuffer buffer(&filedata);
full.save(&buffer, "JPG", 87); QImageWriter writer(&buffer, "JPEG");
writer.setQuality(87);
writer.setProgressiveScanWrite(true);
writer.write(full);
} }
photoThumbs.emplace('m', PreparedPhotoThumb{ .image = medium });
photoSizes.push_back(MTP_photoSize(MTP_string("m"), MTP_fileLocationToBeDeprecated(MTP_long(0), MTP_int(0)), MTP_int(medium.width()), MTP_int(medium.height()), MTP_int(0)));
photoThumbs.emplace('y', PreparedPhotoThumb{
.image = full,
.bytes = filedata
});
photoSizes.push_back(MTP_photoSize(MTP_string("y"), MTP_fileLocationToBeDeprecated(MTP_long(0), MTP_int(0)), MTP_int(full.width()), MTP_int(full.height()), MTP_int(0)));
photo = MTP_photo( photo = MTP_photo(
MTP_flags(0), MTP_flags(0),

View file

@ -219,6 +219,7 @@ struct FileLoadResult {
uint64 thumbId = 0; // id is always file-id of media, thumbId is file-id of thumb ( == id for photos) uint64 thumbId = 0; // id is always file-id of media, thumbId is file-id of thumb ( == id for photos)
QString thumbname; QString thumbname;
UploadFileParts thumbparts; UploadFileParts thumbparts;
QByteArray thumbbytes;
QByteArray thumbmd5; QByteArray thumbmd5;
QImage thumb; QImage thumb;

View file

@ -42,6 +42,7 @@ StreamedFileDownloader::StreamedFileDownloader(
session, session,
toFile, toFile,
size, size,
size,
locationType, locationType,
toCache, toCache,
fromCloud, fromCloud,
@ -107,7 +108,7 @@ void StreamedFileDownloader::requestPart() {
} }
QByteArray StreamedFileDownloader::readLoadedPart(int offset) { QByteArray StreamedFileDownloader::readLoadedPart(int offset) {
Expects(offset >= 0 && offset < _size); Expects(offset >= 0 && offset < _fullSize);
Expects(!(offset % kPartSize)); Expects(!(offset % kPartSize));
const auto index = (offset / kPartSize); const auto index = (offset / kPartSize);

View file

@ -270,13 +270,15 @@ ImageWithLocation FromPhotoSize(
ImageWithLocation FromImageInMemory( ImageWithLocation FromImageInMemory(
const QImage &image, const QImage &image,
const char *format) { const char *format,
QByteArray bytes) {
if (image.isNull()) { if (image.isNull()) {
return ImageWithLocation(); return ImageWithLocation();
} }
auto bytes = QByteArray(); if (bytes.isEmpty()) {
auto buffer = QBuffer(&bytes); auto buffer = QBuffer(&bytes);
image.save(&buffer, format); image.save(&buffer, format);
}
return ImageWithLocation{ return ImageWithLocation{
.location = ImageLocation( .location = ImageLocation(
DownloadLocation{ InMemoryLocation{ bytes } }, DownloadLocation{ InMemoryLocation{ bytes } },

View file

@ -41,7 +41,8 @@ namespace Images {
const MTPVideoSize &size); const MTPVideoSize &size);
[[nodiscard]] ImageWithLocation FromImageInMemory( [[nodiscard]] ImageWithLocation FromImageInMemory(
const QImage &image, const QImage &image,
const char *format); const char *format,
QByteArray bytes = QByteArray());
[[nodiscard]] ImageLocation FromWebDocument(const MTPWebDocument &document); [[nodiscard]] ImageLocation FromWebDocument(const MTPWebDocument &document);
} // namespace Images } // namespace Images

View file

@ -462,7 +462,9 @@ SendMediaReady PrepareWallPaper(MTP::DcId dcId, const QImage &image) {
MTP_fileLocationToBeDeprecated(MTP_long(0), MTP_int(0)), MTP_fileLocationToBeDeprecated(MTP_long(0), MTP_int(0)),
MTP_int(image.width()), MTP_int(image.width()),
MTP_int(image.height()), MTP_int(0))); MTP_int(image.height()), MTP_int(0)));
thumbnails.emplace(type[0], std::move(image)); thumbnails.emplace(
type[0],
PreparedPhotoThumb{ .image = std::move(image) });
}; };
push("s", scaled(320)); push("s", scaled(320));

View file

@ -426,15 +426,21 @@ SendMediaReady PrepareThemeMedia(
thumbnail.save(&buffer, "JPG", 87); thumbnail.save(&buffer, "JPG", 87);
} }
const auto push = [&](const char *type, QImage &&image) { const auto push = [&](
const char *type,
QImage &&image,
QByteArray bytes = QByteArray()) {
sizes.push_back(MTP_photoSize( sizes.push_back(MTP_photoSize(
MTP_string(type), MTP_string(type),
MTP_fileLocationToBeDeprecated(MTP_long(0), MTP_int(0)), MTP_fileLocationToBeDeprecated(MTP_long(0), MTP_int(0)),
MTP_int(image.width()), MTP_int(image.width()),
MTP_int(image.height()), MTP_int(0))); MTP_int(image.height()), MTP_int(0)));
thumbnails.emplace(type[0], std::move(image)); thumbnails.emplace(type[0], PreparedPhotoThumb{
.image = std::move(image),
.bytes = std::move(bytes)
});
}; };
push("s", std::move(thumbnail)); push("s", std::move(thumbnail), thumbnailBytes);
const auto filename = base::FileNameFromUserString(name) const auto filename = base::FileNameFromUserString(name)
+ qsl(".tdesktop-theme"); + qsl(".tdesktop-theme");