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

View File

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

View File

@ -104,7 +104,7 @@ bool PhotoData::loading(PhotoSize size) const {
} else if (valid == existing) {
return true;
}
return (_images[valid].loader->fullSize()
return (_images[valid].loader->loadSize()
>= _images[existing].progressivePartSize);
}
@ -113,6 +113,10 @@ bool PhotoData::failed(PhotoSize size) const {
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 {
return _images[validSizeIndex(size)].location;
}
@ -144,10 +148,11 @@ int PhotoData::imageByteSize(PhotoSize size) const {
bool PhotoData::displayLoading() const {
const auto index = PhotoSizeIndex(PhotoSize::Large);
return _images[index].loader
? (!_images[index].loader->loadingLocal()
|| !_images[index].loader->autoLoading())
: (uploading() && !waitingForAlbum());
if (const auto loader = _images[index].loader.get()) {
return !loader->finished()
&& (!loader->loadingLocal() || !loader->autoLoading());
}
return (uploading() && !waitingForAlbum());
}
void PhotoData::cancel() {
@ -262,7 +267,6 @@ void PhotoData::load(
// Could've changed, if the requested size didn't have a location.
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);
@ -270,10 +274,25 @@ void PhotoData::load(
return true;
};
const auto done = [=](QImage result) {
if (const auto active = activeMediaView()) {
active->set(validSize, existingSize, std::move(result));
Expects(_images[valid].loader != nullptr);
// 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);
}
};
@ -453,6 +472,7 @@ void PhotoSaveClickHandler::onClickImpl() const {
if (!data->date) {
return;
} else {
data->clearFailed(PhotoSize::Large);
data->load(context());
}
}

View File

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

View File

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

View File

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

View File

@ -287,7 +287,11 @@ using PollId = uint64;
using WallPaperId = uint64;
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
using VoiceWaveform = QVector<signed char>;

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -32,6 +32,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "app.h"
#include <QtCore/QBuffer>
#include <QtGui/QImageWriter>
namespace {
@ -202,17 +203,23 @@ SendMediaReady PreparePeerPhoto(MTP::DcId dcId, PeerId peerId, QImage &&image) {
Qt::KeepAspectRatio,
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(
MTP_string(type),
MTP_fileLocationToBeDeprecated(MTP_long(0), MTP_int(0)),
MTP_int(image.width()),
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("b", scaled(320));
push("c", std::move(image));
push("c", std::move(image), jpeg);
const auto id = rand_value<PhotoId>();
const auto photo = MTP_photo(
@ -467,6 +474,7 @@ void FileLoadResult::setFileData(const QByteArray &filedata) {
void FileLoadResult::setThumbData(const QByteArray &thumbdata) {
if (!thumbdata.isEmpty()) {
thumbbytes = thumbdata;
int32 size = thumbdata.size();
for (int32 i = 0, part = 0; i < size; i += kPhotoUploadPartSize, ++part) {
thumbparts.insert(part, thumbdata.mid(i, kPhotoUploadPartSize));
@ -864,17 +872,22 @@ void FileLoadTask::process() {
attributes.push_back(MTP_documentAttributeAnimated());
} else if (_type != SendMediaType::File) {
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;
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);
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(
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)
QString thumbname;
UploadFileParts thumbparts;
QByteArray thumbbytes;
QByteArray thumbmd5;
QImage thumb;

View File

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

View File

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

View File

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

View File

@ -426,15 +426,21 @@ SendMediaReady PrepareThemeMedia(
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(
MTP_string(type),
MTP_fileLocationToBeDeprecated(MTP_long(0), MTP_int(0)),
MTP_int(image.width()),
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)
+ qsl(".tdesktop-theme");