Support different location types for thumbnails.

This commit is contained in:
John Preston 2020-04-16 16:37:26 +04:00
parent 37aabc0da9
commit 3797753d16
19 changed files with 491 additions and 148 deletions

View File

@ -924,6 +924,8 @@ PRIVATE
ui/image/image.h
ui/image/image_location.cpp
ui/image/image_location.h
ui/image/image_location_factory.cpp
ui/image/image_location_factory.h
ui/image/image_source.cpp
ui/image/image_source.h
ui/widgets/continuous_sliders.cpp

View File

@ -10,6 +10,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "main/main_session.h"
#include "data/data_session.h"
#include "data/data_document.h"
#include "ui/image/image_location_factory.h"
#include "storage/localimageloader.h"
#include "base/unixtime.h"
#include "apiwrap.h"
@ -112,7 +113,7 @@ void DicePack::tryGenerateLocalZero() {
Assert(result != nullptr);
const auto document = _session->data().processDocument(
result->document,
std::move(result->thumb));
Images::FromImageInMemory(result->thumb, "PNG"));
document->setLocation(FileLocation(path));
_map.emplace(0, document);

View File

@ -606,16 +606,17 @@ bool DocumentData::checkWallPaperProperties() {
void DocumentData::updateThumbnails(
const QByteArray &inlineThumbnailBytes,
const StorageImageLocation &thumbnail) {
const ImageWithLocation &thumbnail) {
if (!inlineThumbnailBytes.isEmpty()
&& _inlineThumbnailBytes.isEmpty()) {
_inlineThumbnailBytes = inlineThumbnailBytes;
}
if (thumbnail.valid()
if (thumbnail.location.valid()
&& (!_thumbnailLocation.valid()
|| _thumbnailLocation.width() < thumbnail.width()
|| _thumbnailLocation.height() < thumbnail.height())) {
_thumbnailLocation = thumbnail;
|| _thumbnailLocation.width() < thumbnail.location.width()
|| _thumbnailLocation.height() < thumbnail.location.height())) {
_thumbnailLocation = thumbnail.location;
_thumbnailByteSize = thumbnail.bytesCount;
if (_thumbnailLoader) {
const auto origin = base::take(_thumbnailLoader)->fileOrigin();
loadThumbnail(origin);
@ -624,7 +625,7 @@ void DocumentData::updateThumbnails(
}
}
const StorageImageLocation &DocumentData::thumbnailLocation() const {
const ImageLocation &DocumentData::thumbnailLocation() const {
return _thumbnailLocation;
}
@ -659,16 +660,17 @@ void DocumentData::loadThumbnail(Data::FileOrigin origin) {
}
}
const auto autoLoading = false;
_thumbnailLoader = std::make_unique<mtpFileLoader>(
_thumbnailLoader = CreateFileLoader(
_thumbnailLocation.file(),
origin,
UnknownFileLocation,
QString(),
_thumbnailSize,
_thumbnailByteSize,
UnknownFileLocation,
LoadToCacheAsWell,
LoadFromCloudOrLocal,
autoLoading,
Data::kImageCacheTag);
_thumbnailLoader->updates(
) | rpl::start_with_error_done([=](bool started) {
_thumbnailLoader = nullptr;
@ -683,6 +685,7 @@ void DocumentData::loadThumbnail(Data::FileOrigin origin) {
}
_thumbnailLoader = nullptr;
}) | rpl::release();
_thumbnailLoader->start();
}

View File

@ -161,8 +161,8 @@ public:
void loadThumbnail(Data::FileOrigin origin);
void updateThumbnails(
const QByteArray &inlineThumbnailBytes,
const StorageImageLocation &thumbnail);
const StorageImageLocation &thumbnailLocation() const;
const ImageWithLocation &thumbnail);
const ImageLocation &thumbnailLocation() const;
[[nodiscard]] QByteArray inlineThumbnailBytes() const {
return _inlineThumbnailBytes;
@ -301,9 +301,9 @@ private:
WebFileLocation _urlLocation;
QByteArray _inlineThumbnailBytes;
StorageImageLocation _thumbnailLocation;
ImageLocation _thumbnailLocation;
std::unique_ptr<FileLoader> _thumbnailLoader;
int _thumbnailSize = 0;
int _thumbnailByteSize = 0;
std::unique_ptr<Data::ReplyPreview> _replyPreview;
std::weak_ptr<Data::DocumentMedia> _media;
PhotoData *_goodThumbnailPhoto = nullptr;

View File

@ -17,6 +17,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "core/crash_reports.h" // CrashReports::SetAnnotation
#include "ui/image/image.h"
#include "ui/image/image_source.h" // Images::LocalFileSource
#include "ui/image/image_location_factory.h" // Images::FromPhotoSize
#include "export/export_controller.h"
#include "export/view/export_view_panel_controller.h"
#include "window/notifications_manager.h"
@ -2355,14 +2356,11 @@ not_null<DocumentData*> Session::document(DocumentId id) {
}
not_null<DocumentData*> Session::processDocument(const MTPDocument &data) {
switch (data.type()) {
case mtpc_document:
return processDocument(data.c_document());
case mtpc_documentEmpty:
return document(data.c_documentEmpty().vid().v);
}
Unexpected("Type in Session::document().");
return data.match([&](const MTPDdocument &data) {
return processDocument(data);
}, [&](const MTPDdocumentEmpty &data) {
return document(data.vid().v);
});
}
not_null<DocumentData*> Session::processDocument(const MTPDdocument &data) {
@ -2373,33 +2371,22 @@ not_null<DocumentData*> Session::processDocument(const MTPDdocument &data) {
not_null<DocumentData*> Session::processDocument(
const MTPdocument &data,
QImage &&thumb) {
switch (data.type()) {
case mtpc_documentEmpty:
return document(data.c_documentEmpty().vid().v);
case mtpc_document: {
const auto &fields = data.c_document();
const auto mime = qs(fields.vmime_type());
// #TODO optimize
const auto format = Core::IsMimeSticker(mime)
? "WEBP"
: "JPG";
Images::Create(std::move(thumb), format);
const ImageWithLocation &thumbnail) {
return data.match([&](const MTPDdocument &data) {
return document(
fields.vid().v,
fields.vaccess_hash().v,
fields.vfile_reference().v,
fields.vdate().v,
fields.vattributes().v,
mime,
data.vid().v,
data.vaccess_hash().v,
data.vfile_reference().v,
data.vdate().v,
data.vattributes().v,
qs(data.vmime_type()),
QByteArray(),
StorageImageLocation(),
fields.vdc_id().v,
fields.vsize().v);
} break;
}
Unexpected("Type in Session::document() with thumb.");
thumbnail,
data.vdc_id().v,
data.vsize().v);
}, [&](const MTPDdocumentEmpty &data) {
return document(data.vid().v);
});
}
not_null<DocumentData*> Session::document(
@ -2410,7 +2397,7 @@ not_null<DocumentData*> Session::document(
const QVector<MTPDocumentAttribute> &attributes,
const QString &mime,
const QByteArray &inlineThumbnailBytes,
const StorageImageLocation &thumbnailLocation,
const ImageWithLocation &thumbnail,
int32 dc,
int32 size) {
const auto result = document(id);
@ -2422,7 +2409,7 @@ not_null<DocumentData*> Session::document(
attributes,
mime,
inlineThumbnailBytes,
thumbnailLocation,
thumbnail,
dc,
size);
return result;
@ -2473,22 +2460,15 @@ void Session::documentConvert(
DocumentData *Session::documentFromWeb(
const MTPWebDocument &data,
ImagePtr thumb) {
switch (data.type()) {
case mtpc_webDocument:
return documentFromWeb(data.c_webDocument(), thumb);
case mtpc_webDocumentNoProxy:
return documentFromWeb(data.c_webDocumentNoProxy(), thumb);
}
Unexpected("Type in Session::documentFromWeb.");
const ImageLocation &thumbnailLocation) {
return data.match([&](const auto &data) {
return documentFromWeb(data, thumbnailLocation);
});
}
DocumentData *Session::documentFromWeb(
const MTPDwebDocument &data,
ImagePtr thumb) {
// #TODO optimize thumb
const ImageLocation &thumbnailLocation) {
const auto result = document(
rand_value<DocumentId>(),
uint64(0),
@ -2497,7 +2477,7 @@ DocumentData *Session::documentFromWeb(
data.vattributes().v,
data.vmime_type().v,
QByteArray(),
StorageImageLocation(),
ImageWithLocation{ .location = thumbnailLocation },
MTP::maindc(),
int32(0)); // data.vsize().v
result->setWebLocation(WebFileLocation(
@ -2508,8 +2488,7 @@ DocumentData *Session::documentFromWeb(
DocumentData *Session::documentFromWeb(
const MTPDwebDocumentNoProxy &data,
ImagePtr thumb) {
// #TODO optimize thumb
const ImageLocation &thumbnailLocation) {
const auto result = document(
rand_value<DocumentId>(),
uint64(0),
@ -2518,7 +2497,7 @@ DocumentData *Session::documentFromWeb(
data.vattributes().v,
data.vmime_type().v,
QByteArray(),
StorageImageLocation(),
ImageWithLocation{ .location = thumbnailLocation },
MTP::maindc(),
int32(0)); // data.vsize().v
result->setContentUrl(qs(data.vurl()));
@ -2538,8 +2517,10 @@ void Session::documentApplyFields(
const MTPDdocument &data) {
const auto inlineThumbnailBytes = FindDocumentInlineThumbnail(data);
const auto thumbnailSize = FindDocumentThumbnail(data);
// #TODO optimize
const auto thumbnail = Images::Create(data, thumbnailSize)->location();
const auto prepared = Images::FromPhotoSize(
_session,
data,
thumbnailSize);
documentApplyFields(
document,
data.vaccess_hash().v,
@ -2548,7 +2529,7 @@ void Session::documentApplyFields(
data.vattributes().v,
qs(data.vmime_type()),
inlineThumbnailBytes,
thumbnail,
prepared,
data.vdc_id().v,
data.vsize().v);
}
@ -2561,7 +2542,7 @@ void Session::documentApplyFields(
const QVector<MTPDocumentAttribute> &attributes,
const QString &mime,
const QByteArray &inlineThumbnailBytes,
const StorageImageLocation &thumbnailLocation,
const ImageWithLocation &thumbnail,
int32 dc,
int32 size) {
if (!date) {
@ -2569,7 +2550,7 @@ void Session::documentApplyFields(
}
document->date = date;
document->setMimeString(mime);
document->updateThumbnails(inlineThumbnailBytes, thumbnailLocation);
document->updateThumbnails(inlineThumbnailBytes, thumbnail);
document->size = size;
document->setattributes(attributes);

View File

@ -490,7 +490,7 @@ public:
not_null<DocumentData*> processDocument(const MTPDdocument &data);
not_null<DocumentData*> processDocument(
const MTPdocument &data,
QImage &&thumb);
const ImageWithLocation &thumbnail);
[[nodiscard]] not_null<DocumentData*> document(
DocumentId id,
const uint64 &access,
@ -499,7 +499,7 @@ public:
const QVector<MTPDocumentAttribute> &attributes,
const QString &mime,
const QByteArray &inlineThumbnailBytes,
const StorageImageLocation &thumbnailLocation,
const ImageWithLocation &thumbnail,
int32 dc,
int32 size);
void documentConvert(
@ -507,7 +507,7 @@ public:
const MTPDocument &data);
[[nodiscard]] DocumentData *documentFromWeb(
const MTPWebDocument &data,
ImagePtr thumb);
const ImageLocation &thumbnailLocation);
[[nodiscard]] not_null<WebPageData*> webpage(WebPageId id);
not_null<WebPageData*> processWebpage(const MTPWebPage &data);
@ -753,15 +753,15 @@ private:
const QVector<MTPDocumentAttribute> &attributes,
const QString &mime,
const QByteArray &inlineThumbnailBytes,
const StorageImageLocation &thumbnailLocation,
const ImageWithLocation &thumbnail,
int32 dc,
int32 size);
DocumentData *documentFromWeb(
const MTPDwebDocument &data,
ImagePtr thumb);
const ImageLocation &thumbnailLocation);
DocumentData *documentFromWeb(
const MTPDwebDocumentNoProxy &data,
ImagePtr thumb);
const ImageLocation &thumbnailLocation);
void webpageApplyFields(
not_null<WebPageData*> page,

View File

@ -19,6 +19,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "core/file_utilities.h"
#include "core/mime_type.h"
#include "ui/image/image.h"
#include "ui/image/image_location_factory.h"
#include "mainwidget.h"
#include "main/main_session.h"
@ -40,7 +41,9 @@ QString GetContentUrl(const MTPWebDocument &document) {
Result::Result(const Creator &creator) : _queryId(creator.queryId), _type(creator.type) {
}
std::unique_ptr<Result> Result::create(uint64 queryId, const MTPBotInlineResult &mtpData) {
std::unique_ptr<Result> Result::create(
uint64 queryId,
const MTPBotInlineResult &mtpData) {
using StringToTypeMap = QMap<QString, Result::Type>;
static StaticNeverFreedPointer<StringToTypeMap> stringToTypeMap{ ([]() -> StringToTypeMap* {
auto result = std::make_unique<StringToTypeMap>();
@ -94,7 +97,9 @@ std::unique_ptr<Result> Result::create(uint64 queryId, const MTPBotInlineResult
} else {
result->_document = Auth().data().documentFromWeb(
result->adjustAttributes(*content),
result->_thumb);
(r.vthumb()
? Images::FromWebDocument(*r.vthumb())
: ImageLocation()));
}
}
message = &r.vsend_message();

View File

@ -14,6 +14,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "mainwindow.h"
#include "core/application.h"
#include "storage/localstorage.h"
#include "storage/file_download_mtproto.h"
#include "storage/file_download_web.h"
#include "platform/platform_file_utilities.h"
#include "main/main_session.h"
#include "apiwrap.h"
@ -23,6 +25,67 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "facades.h"
#include "app.h"
namespace {
class FromMemoryLoader final : public FileLoader {
public:
FromMemoryLoader(
const QByteArray &data,
const QString &toFile,
int32 size,
LocationType locationType,
LoadToCacheSetting toCache,
LoadFromCloudSetting fromCloud,
bool autoLoading,
uint8 cacheTag);
private:
Storage::Cache::Key cacheKey() const override;
std::optional<MediaKey> fileLocationKey() const override;
void cancelHook() override;
void startLoading() override;
QByteArray _data;
};
FromMemoryLoader::FromMemoryLoader(
const QByteArray &data,
const QString &toFile,
int32 size,
LocationType locationType,
LoadToCacheSetting toCache,
LoadFromCloudSetting fromCloud,
bool autoLoading,
uint8 cacheTag
) : FileLoader(
toFile,
size,
locationType,
toCache,
fromCloud,
autoLoading,
cacheTag)
, _data(data) {
}
Storage::Cache::Key FromMemoryLoader::cacheKey() const {
return {};
}
std::optional<MediaKey> FromMemoryLoader::fileLocationKey() const {
return std::nullopt;
}
void FromMemoryLoader::cancelHook() {
}
void FromMemoryLoader::startLoading() {
finishWithBytes(_data);
}
} // namespace
FileLoader::FileLoader(
const QString &toFile,
int32 size,
@ -220,14 +283,14 @@ bool FileLoader::tryLoadLocal() {
return true;
}
const auto weak = base::make_weak(this);
if (_toCache == LoadToCacheAsWell) {
loadLocal(cacheKey());
notifyAboutProgress();
const auto key = cacheKey();
if (key.low || key.high) {
loadLocal(key);
notifyAboutProgress();
}
}
if (!weak) {
return false;
} else if (_localStatus != LocalStatus::NotTried) {
if (_localStatus != LocalStatus::NotTried) {
return _finished;
} else if (_localLoading) {
_localStatus = LocalStatus::Loading;
@ -361,8 +424,10 @@ bool FileLoader::finalizeResult() {
Local::writeFileLocation(*key, FileLocation(_filename));
}
}
const auto key = cacheKey();
if ((_toCache == LoadToCacheAsWell)
&& (_data.size() <= Storage::kMaxFileInMemory)) {
&& (_data.size() <= Storage::kMaxFileInMemory)
&& (key.low || key.high)) {
_session->data().cache().put(
cacheKey(),
Storage::Cache::Database::TaggedValue(
@ -374,3 +439,62 @@ bool FileLoader::finalizeResult() {
_updates.fire_done();
return true;
}
std::unique_ptr<FileLoader> CreateFileLoader(
const DownloadLocation &location,
Data::FileOrigin origin,
const QString &toFile,
int size,
LocationType locationType,
LoadToCacheSetting toCache,
LoadFromCloudSetting fromCloud,
bool autoLoading,
uint8 cacheTag) {
auto result = std::unique_ptr<FileLoader>();
location.data.match([&](const StorageFileLocation &data) {
result = std::make_unique<mtpFileLoader>(
data,
origin,
locationType,
toFile,
size,
toCache,
fromCloud,
autoLoading,
cacheTag);
}, [&](const WebFileLocation &data) {
result = std::make_unique<mtpFileLoader>(
data,
size,
fromCloud,
autoLoading,
cacheTag);
}, [&](const GeoPointLocation &data) {
result = std::make_unique<mtpFileLoader>(
data,
size,
fromCloud,
autoLoading,
cacheTag);
}, [&](const PlainUrlLocation &data) {
result = std::make_unique<webFileLoader>(
data.url,
toFile,
fromCloud,
autoLoading,
cacheTag);
}, [&](const InMemoryLocation &data) {
result = std::make_unique<FromMemoryLoader>(
data.bytes,
toFile,
size,
locationType,
toCache,
fromCloud,
autoLoading,
cacheTag);
});
Ensures(result != nullptr);
return result;
}

View File

@ -164,3 +164,14 @@ protected:
mutable QImage _imageData;
};
[[nodiscard]] std::unique_ptr<FileLoader> CreateFileLoader(
const DownloadLocation &location,
Data::FileOrigin origin,
const QString &toFile,
int size,
LocationType locationType,
LoadToCacheSetting toCache,
LoadFromCloudSetting fromCloud,
bool autoLoading,
uint8 cacheTag);

View File

@ -13,6 +13,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "data/data_document_media.h"
#include "data/data_photo.h"
#include "data/data_session.h"
#include "ui/image/image_location_factory.h"
#include "core/mime_type.h"
#include "main/main_session.h"
namespace Storage {
@ -44,6 +46,10 @@ constexpr auto kUploadRequestInterval = crl::time(500);
// How much time without upload causes additional session kill.
constexpr auto kKillSessionTimeout = 15 * crl::time(000);
[[nodiscard]] const char *ThumbnailFormat(const QString &mime) {
return Core::IsMimeSticker(mime) ? "WEBP" : "JPG";
}
} // namespace
struct Uploader::File {
@ -160,7 +166,9 @@ void Uploader::uploadMedia(
? Auth().data().processDocument(media.document)
: Auth().data().processDocument(
media.document,
base::duplicate(media.photoThumbs.front().second));
Images::FromImageInMemory(
media.photoThumbs.front().second,
"JPG"));
if (!media.data.isEmpty()) {
document->setDataAndCache(media.data);
if (media.type == SendMediaType::ThemeFile) {
@ -191,13 +199,18 @@ void Uploader::upload(
? Auth().data().processDocument(file->document)
: Auth().data().processDocument(
file->document,
std::move(file->thumb));
Images::FromImageInMemory(
file->thumb,
ThumbnailFormat(file->filemime)));
document->uploadingData = std::make_unique<Data::UploadState>(
document->size);
if (!file->goodThumbnail.isNull()) {
if (const auto active = document->activeMediaView()) {
if (const auto active = document->activeMediaView()) {
if (!file->goodThumbnail.isNull()) {
active->setGoodThumbnail(std::move(file->goodThumbnail));
}
if (!file->thumb.isNull()) {
active->setThumbnail(file->thumb);
}
}
if (!file->goodThumbnailBytes.isEmpty()) {
document->owner().cache().putIfEmpty(
@ -208,13 +221,13 @@ void Uploader::upload(
}
if (!file->content.isEmpty()) {
document->setDataAndCache(file->content);
if (file->type == SendMediaType::ThemeFile) {
document->checkWallPaperProperties();
}
}
if (!file->filepath.isEmpty()) {
document->setLocation(FileLocation(file->filepath));
}
if (file->type == SendMediaType::ThemeFile) {
document->checkWallPaperProperties();
}
}
queue.emplace(msgId, File(file));
sendNext();

View File

@ -3945,7 +3945,7 @@ void importOldRecentStickers() {
attributes,
mime,
QByteArray(),
StorageImageLocation(),
ImageWithLocation(),
dc,
size);
if (!doc->sticker()) {

View File

@ -96,6 +96,34 @@ std::optional<StorageImageLocation> readStorageImageLocation(
: std::nullopt;
}
int imageLocationSize(const ImageLocation &location) {
// Modern image location tag + (size + content) of the serialization.
return sizeof(qint32) * 2 + location.serializeSize();
}
void writeImageLocation(QDataStream &stream, const ImageLocation &location) {
stream << kModernImageLocationTag << location.serialize();
}
std::optional<ImageLocation> readImageLocation(
int streamAppVersion,
QDataStream &stream) {
const auto legacy = readLegacyStorageImageLocationOrTag(
streamAppVersion,
stream);
if (legacy) {
return ImageLocation(
DownloadLocation{ legacy->file() },
legacy->width(),
legacy->height());
}
auto serialized = QByteArray();
stream >> serialized;
return (stream.status() == QDataStream::Ok)
? ImageLocation::FromSerialized(serialized)
: std::nullopt;
}
uint32 peerSize(not_null<PeerData*> peer) {
uint32 result = sizeof(quint64)
+ sizeof(quint64)

View File

@ -102,6 +102,15 @@ std::optional<StorageImageLocation> readStorageImageLocation(
int streamAppVersion,
QDataStream &stream);
int imageLocationSize(const ImageLocation &location);
void writeImageLocation(QDataStream &stream, const ImageLocation &location);
// NB! This method can return StorageFileLocation with Type::Generic!
// The reader should discard it or convert to one of the valid modern types.
std::optional<ImageLocation> readImageLocation(
int streamAppVersion,
QDataStream &stream);
template <typename T>
inline T read(QDataStream &stream) {
auto result = T();

View File

@ -46,11 +46,10 @@ void Document::writeToStream(QDataStream &stream, DocumentData *document) {
stream << qint32(StickerSetTypeEmpty);
} break;
}
writeStorageImageLocation(stream, document->_thumbnailLocation);
} else {
stream << qint32(document->getDuration());
writeStorageImageLocation(stream, document->thumbnailLocation());
}
writeImageLocation(stream, document->thumbnailLocation());
}
DocumentData *Document::readFromStreamHelper(int streamAppVersion, QDataStream &stream, const StickerSetInfo *info) {
@ -77,14 +76,11 @@ DocumentData *Document::readFromStreamHelper(int streamAppVersion, QDataStream &
}
qint32 duration = -1;
std::optional<StorageImageLocation> thumb;
std::optional<ImageLocation> thumb;
if (type == StickerDocument) {
QString alt;
qint32 typeOfSet;
stream >> alt >> typeOfSet;
thumb = readStorageImageLocation(streamAppVersion, stream);
if (typeOfSet == StickerSetTypeEmpty) {
attributes.push_back(MTP_documentAttributeSticker(MTP_flags(0), MTP_string(alt), MTP_inputStickerSetEmpty(), MTPMaskCoords()));
} else if (info) {
@ -113,8 +109,8 @@ DocumentData *Document::readFromStreamHelper(int streamAppVersion, QDataStream &
if (type == AnimatedDocument) {
attributes.push_back(MTP_documentAttributeAnimated());
}
thumb = readStorageImageLocation(streamAppVersion, stream);
}
thumb = readImageLocation(streamAppVersion, stream);
if (width > 0 && height > 0) {
if (duration >= 0) {
auto flags = MTPDdocumentAttributeVideo::Flags(0);
@ -127,9 +123,12 @@ DocumentData *Document::readFromStreamHelper(int streamAppVersion, QDataStream &
}
}
const auto storage = base::get_if<StorageFileLocation>(
&thumb->file().data);
if ((!dc && !access)
|| !thumb
|| (thumb->valid() && !thumb->file().isDocumentThumbnail())) {
|| (thumb->valid()
&& (!storage || !storage->isDocumentThumbnail()))) {
stream.setStatus(QDataStream::ReadCorruptData);
// We can't convert legacy thumbnail location to modern, because
// size letter ('s' or 'm') is lost, it was not saved in legacy.
@ -143,7 +142,7 @@ DocumentData *Document::readFromStreamHelper(int streamAppVersion, QDataStream &
attributes,
mime,
QByteArray(),
*thumb,
ImageWithLocation{ .location = *thumb },
dc,
size);
}
@ -176,7 +175,7 @@ int Document::sizeInStream(DocumentData *document) {
result += sizeof(qint32);
}
// + thumb loc
result += Serialize::storageImageLocationSize(document->thumbnailLocation());
result += Serialize::imageLocationSize(document->thumbnailLocation());
return result;
}

View File

@ -61,54 +61,57 @@ uint64 SinglePixKey(Options options) {
} // namespace
QImage FromInlineBytes(const QByteArray &bytes) {
QByteArray ExpandInlineBytes(const QByteArray &bytes) {
if (bytes.size() < 3 || bytes[0] != '\x01') {
return QImage();
return QByteArray();
}
const char header[] = "\xff\xd8\xff\xe0\x00\x10\x4a\x46\x49"
"\x46\x00\x01\x01\x00\x00\x01\x00\x01\x00\x00\xff\xdb\x00\x43\x00\x28\x1c"
"\x1e\x23\x1e\x19\x28\x23\x21\x23\x2d\x2b\x28\x30\x3c\x64\x41\x3c\x37\x37"
"\x3c\x7b\x58\x5d\x49\x64\x91\x80\x99\x96\x8f\x80\x8c\x8a\xa0\xb4\xe6\xc3"
"\xa0\xaa\xda\xad\x8a\x8c\xc8\xff\xcb\xda\xee\xf5\xff\xff\xff\x9b\xc1\xff"
"\xff\xff\xfa\xff\xe6\xfd\xff\xf8\xff\xdb\x00\x43\x01\x2b\x2d\x2d\x3c\x35"
"\x3c\x76\x41\x41\x76\xf8\xa5\x8c\xa5\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8"
"\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8"
"\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8"
"\xf8\xf8\xf8\xf8\xf8\xff\xc0\x00\x11\x08\x00\x00\x00\x00\x03\x01\x22\x00"
"\x02\x11\x01\x03\x11\x01\xff\xc4\x00\x1f\x00\x00\x01\x05\x01\x01\x01\x01"
"\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x01\x02\x03\x04\x05\x06\x07\x08"
"\x09\x0a\x0b\xff\xc4\x00\xb5\x10\x00\x02\x01\x03\x03\x02\x04\x03\x05\x05"
"\x04\x04\x00\x00\x01\x7d\x01\x02\x03\x00\x04\x11\x05\x12\x21\x31\x41\x06"
"\x13\x51\x61\x07\x22\x71\x14\x32\x81\x91\xa1\x08\x23\x42\xb1\xc1\x15\x52"
"\xd1\xf0\x24\x33\x62\x72\x82\x09\x0a\x16\x17\x18\x19\x1a\x25\x26\x27\x28"
"\x29\x2a\x34\x35\x36\x37\x38\x39\x3a\x43\x44\x45\x46\x47\x48\x49\x4a\x53"
"\x54\x55\x56\x57\x58\x59\x5a\x63\x64\x65\x66\x67\x68\x69\x6a\x73\x74\x75"
"\x76\x77\x78\x79\x7a\x83\x84\x85\x86\x87\x88\x89\x8a\x92\x93\x94\x95\x96"
"\x97\x98\x99\x9a\xa2\xa3\xa4\xa5\xa6\xa7\xa8\xa9\xaa\xb2\xb3\xb4\xb5\xb6"
"\xb7\xb8\xb9\xba\xc2\xc3\xc4\xc5\xc6\xc7\xc8\xc9\xca\xd2\xd3\xd4\xd5\xd6"
"\xd7\xd8\xd9\xda\xe1\xe2\xe3\xe4\xe5\xe6\xe7\xe8\xe9\xea\xf1\xf2\xf3\xf4"
"\xf5\xf6\xf7\xf8\xf9\xfa\xff\xc4\x00\x1f\x01\x00\x03\x01\x01\x01\x01\x01"
"\x01\x01\x01\x01\x00\x00\x00\x00\x00\x00\x01\x02\x03\x04\x05\x06\x07\x08"
"\x09\x0a\x0b\xff\xc4\x00\xb5\x11\x00\x02\x01\x02\x04\x04\x03\x04\x07\x05"
"\x04\x04\x00\x01\x02\x77\x00\x01\x02\x03\x11\x04\x05\x21\x31\x06\x12\x41"
"\x51\x07\x61\x71\x13\x22\x32\x81\x08\x14\x42\x91\xa1\xb1\xc1\x09\x23\x33"
"\x52\xf0\x15\x62\x72\xd1\x0a\x16\x24\x34\xe1\x25\xf1\x17\x18\x19\x1a\x26"
"\x27\x28\x29\x2a\x35\x36\x37\x38\x39\x3a\x43\x44\x45\x46\x47\x48\x49\x4a"
"\x53\x54\x55\x56\x57\x58\x59\x5a\x63\x64\x65\x66\x67\x68\x69\x6a\x73\x74"
"\x75\x76\x77\x78\x79\x7a\x82\x83\x84\x85\x86\x87\x88\x89\x8a\x92\x93\x94"
"\x95\x96\x97\x98\x99\x9a\xa2\xa3\xa4\xa5\xa6\xa7\xa8\xa9\xaa\xb2\xb3\xb4"
"\xb5\xb6\xb7\xb8\xb9\xba\xc2\xc3\xc4\xc5\xc6\xc7\xc8\xc9\xca\xd2\xd3\xd4"
"\xd5\xd6\xd7\xd8\xd9\xda\xe2\xe3\xe4\xe5\xe6\xe7\xe8\xe9\xea\xf2\xf3\xf4"
"\xf5\xf6\xf7\xf8\xf9\xfa\xff\xda\x00\x0c\x03\x01\x00\x02\x11\x03\x11\x00"
"\x3f\x00";
"\x46\x00\x01\x01\x00\x00\x01\x00\x01\x00\x00\xff\xdb\x00\x43\x00\x28\x1c"
"\x1e\x23\x1e\x19\x28\x23\x21\x23\x2d\x2b\x28\x30\x3c\x64\x41\x3c\x37\x37"
"\x3c\x7b\x58\x5d\x49\x64\x91\x80\x99\x96\x8f\x80\x8c\x8a\xa0\xb4\xe6\xc3"
"\xa0\xaa\xda\xad\x8a\x8c\xc8\xff\xcb\xda\xee\xf5\xff\xff\xff\x9b\xc1\xff"
"\xff\xff\xfa\xff\xe6\xfd\xff\xf8\xff\xdb\x00\x43\x01\x2b\x2d\x2d\x3c\x35"
"\x3c\x76\x41\x41\x76\xf8\xa5\x8c\xa5\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8"
"\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8"
"\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8"
"\xf8\xf8\xf8\xf8\xf8\xff\xc0\x00\x11\x08\x00\x00\x00\x00\x03\x01\x22\x00"
"\x02\x11\x01\x03\x11\x01\xff\xc4\x00\x1f\x00\x00\x01\x05\x01\x01\x01\x01"
"\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x01\x02\x03\x04\x05\x06\x07\x08"
"\x09\x0a\x0b\xff\xc4\x00\xb5\x10\x00\x02\x01\x03\x03\x02\x04\x03\x05\x05"
"\x04\x04\x00\x00\x01\x7d\x01\x02\x03\x00\x04\x11\x05\x12\x21\x31\x41\x06"
"\x13\x51\x61\x07\x22\x71\x14\x32\x81\x91\xa1\x08\x23\x42\xb1\xc1\x15\x52"
"\xd1\xf0\x24\x33\x62\x72\x82\x09\x0a\x16\x17\x18\x19\x1a\x25\x26\x27\x28"
"\x29\x2a\x34\x35\x36\x37\x38\x39\x3a\x43\x44\x45\x46\x47\x48\x49\x4a\x53"
"\x54\x55\x56\x57\x58\x59\x5a\x63\x64\x65\x66\x67\x68\x69\x6a\x73\x74\x75"
"\x76\x77\x78\x79\x7a\x83\x84\x85\x86\x87\x88\x89\x8a\x92\x93\x94\x95\x96"
"\x97\x98\x99\x9a\xa2\xa3\xa4\xa5\xa6\xa7\xa8\xa9\xaa\xb2\xb3\xb4\xb5\xb6"
"\xb7\xb8\xb9\xba\xc2\xc3\xc4\xc5\xc6\xc7\xc8\xc9\xca\xd2\xd3\xd4\xd5\xd6"
"\xd7\xd8\xd9\xda\xe1\xe2\xe3\xe4\xe5\xe6\xe7\xe8\xe9\xea\xf1\xf2\xf3\xf4"
"\xf5\xf6\xf7\xf8\xf9\xfa\xff\xc4\x00\x1f\x01\x00\x03\x01\x01\x01\x01\x01"
"\x01\x01\x01\x01\x00\x00\x00\x00\x00\x00\x01\x02\x03\x04\x05\x06\x07\x08"
"\x09\x0a\x0b\xff\xc4\x00\xb5\x11\x00\x02\x01\x02\x04\x04\x03\x04\x07\x05"
"\x04\x04\x00\x01\x02\x77\x00\x01\x02\x03\x11\x04\x05\x21\x31\x06\x12\x41"
"\x51\x07\x61\x71\x13\x22\x32\x81\x08\x14\x42\x91\xa1\xb1\xc1\x09\x23\x33"
"\x52\xf0\x15\x62\x72\xd1\x0a\x16\x24\x34\xe1\x25\xf1\x17\x18\x19\x1a\x26"
"\x27\x28\x29\x2a\x35\x36\x37\x38\x39\x3a\x43\x44\x45\x46\x47\x48\x49\x4a"
"\x53\x54\x55\x56\x57\x58\x59\x5a\x63\x64\x65\x66\x67\x68\x69\x6a\x73\x74"
"\x75\x76\x77\x78\x79\x7a\x82\x83\x84\x85\x86\x87\x88\x89\x8a\x92\x93\x94"
"\x95\x96\x97\x98\x99\x9a\xa2\xa3\xa4\xa5\xa6\xa7\xa8\xa9\xaa\xb2\xb3\xb4"
"\xb5\xb6\xb7\xb8\xb9\xba\xc2\xc3\xc4\xc5\xc6\xc7\xc8\xc9\xca\xd2\xd3\xd4"
"\xd5\xd6\xd7\xd8\xd9\xda\xe2\xe3\xe4\xe5\xe6\xe7\xe8\xe9\xea\xf2\xf3\xf4"
"\xf5\xf6\xf7\xf8\xf9\xfa\xff\xda\x00\x0c\x03\x01\x00\x02\x11\x03\x11\x00"
"\x3f\x00";
const char footer[] = "\xff\xd9";
auto real = QByteArray(header, sizeof(header) - 1);
real[164] = bytes[1];
real[166] = bytes[2];
const auto ready = real
return real
+ bytes.mid(3)
+ QByteArray::fromRawData(footer, sizeof(footer) - 1);
return App::readImage(ready);
}
QImage FromInlineBytes(const QByteArray &bytes) {
return App::readImage(ExpandInlineBytes(bytes));
}
void ClearRemote() {
@ -364,7 +367,7 @@ ImagePtr Create(const MTPDdocument &document, const MTPPhotoSize &size) {
return CreateFromPhotoSize(create, size);
}
QSize getImageSize(const QVector<MTPDocumentAttribute> &attributes) {
QSize GetSizeForDocument(const QVector<MTPDocumentAttribute> &attributes) {
for (const auto &attribute : attributes) {
if (attribute.type() == mtpc_documentAttributeImageSize) {
auto &size = attribute.c_documentAttributeImageSize();
@ -375,7 +378,7 @@ QSize getImageSize(const QVector<MTPDocumentAttribute> &attributes) {
}
ImagePtr Create(const MTPDwebDocument &document) {
const auto size = getImageSize(document.vattributes().v);
const auto size = GetSizeForDocument(document.vattributes().v);
if (size.isEmpty()) {
return ImagePtr();
}
@ -393,7 +396,7 @@ ImagePtr Create(const MTPDwebDocument &document) {
}
ImagePtr Create(const MTPDwebDocumentNoProxy &document) {
const auto size = getImageSize(document.vattributes().v);
const auto size = GetSizeForDocument(document.vattributes().v);
if (size.isEmpty()) {
return ImagePtr();
}
@ -402,7 +405,7 @@ ImagePtr Create(const MTPDwebDocumentNoProxy &document) {
}
ImagePtr Create(const MTPDwebDocument &document, QSize box) {
//const auto size = getImageSize(document.vattributes().v);
//const auto size = GetSizeForDocument(document.vattributes().v);
//if (size.isEmpty()) {
// return ImagePtr();
//}
@ -419,7 +422,7 @@ ImagePtr Create(const MTPDwebDocument &document, QSize box) {
}
ImagePtr Create(const MTPDwebDocumentNoProxy &document, QSize box) {
//const auto size = getImageSize(document.vattributes().v);
//const auto size = GetSizeForDocument(document.vattributes().v);
//if (size.isEmpty()) {
// return ImagePtr();
//}

View File

@ -13,11 +13,15 @@ class HistoryItem;
namespace Images {
[[nodiscard]] QByteArray ExpandInlineBytes(const QByteArray &bytes);
[[nodiscard]] QImage FromInlineBytes(const QByteArray &bytes);
void ClearRemote();
void ClearAll();
[[nodiscard]] QSize GetSizeForDocument(
const QVector<MTPDocumentAttribute> &attributes);
ImagePtr Create(const QString &file, QByteArray format);
ImagePtr Create(const QString &url, QSize box);
ImagePtr Create(const QString &url, int width, int height);

View File

@ -504,6 +504,13 @@ private:
};
struct ImageWithLocation {
ImageLocation location;
int bytesCount = 0;
QByteArray bytes;
QImage preloaded;
};
class Image;
class ImagePtr {
public:
@ -599,7 +606,9 @@ private:
};
inline bool operator==(const FileLocation &a, const FileLocation &b) {
return (a.name() == b.name()) && (a.modified == b.modified) && (a.size == b.size);
return (a.name() == b.name())
&& (a.modified == b.modified)
&& (a.size == b.size);
}
inline bool operator!=(const FileLocation &a, const FileLocation &b) {
return !(a == b);

View File

@ -0,0 +1,124 @@
/*
This file is part of Telegram Desktop,
the official desktop application for the Telegram messaging service.
For license and copyright information please follow this link:
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#include "ui/image/image_location_factory.h"
#include "ui/image/image.h"
#include "main/main_session.h"
#include <QtCore/QBuffer>
namespace Images {
ImageWithLocation FromPhotoSize(
not_null<Main::Session*> session,
const MTPDdocument &document,
const MTPPhotoSize &size) {
return size.match([&](const MTPDphotoSize &data) {
return ImageWithLocation{
.location = ImageLocation(
DownloadLocation{ StorageFileLocation(
document.vdc_id().v,
session->userId(),
MTP_inputDocumentFileLocation(
document.vid(),
document.vaccess_hash(),
document.vfile_reference(),
data.vtype())) },
data.vw().v,
data.vh().v),
.bytesCount = data.vsize().v
};
}, [&](const MTPDphotoCachedSize &data) {
const auto bytes = qba(data.vbytes());
return ImageWithLocation{
.location = ImageLocation(
DownloadLocation{ StorageFileLocation(
document.vdc_id().v,
session->userId(),
MTP_inputDocumentFileLocation(
document.vid(),
document.vaccess_hash(),
document.vfile_reference(),
data.vtype())) },
data.vw().v,
data.vh().v),
.bytesCount = bytes.size(),
.bytes = bytes
};
}, [&](const MTPDphotoStrippedSize &data) {
return ImageWithLocation();
//const auto bytes = ExpandInlineBytes(qba(data.vbytes()));
//return ImageWithLocation{
// .location = ImageLocation(
// DownloadLocation{ StorageFileLocation(
// document.vdc_id().v,
// session->userId(),
// MTP_inputDocumentFileLocation(
// document.vid(),
// document.vaccess_hash(),
// document.vfile_reference(),
// data.vtype())) },
// width, // ???
// height), // ???
// .bytesCount = bytes.size(),
// .bytes = bytes
//};
}, [&](const MTPDphotoSizeEmpty &) {
return ImageWithLocation();
});
}
ImageWithLocation FromImageInMemory(
const QImage &image,
const char *format) {
if (image.isNull()) {
return ImageWithLocation();
}
auto bytes = QByteArray();
auto buffer = QBuffer(&bytes);
image.save(&buffer, format);
return ImageWithLocation{
.location = ImageLocation(
DownloadLocation{ InMemoryLocation{ bytes } },
image.width(),
image.height()),
.bytesCount = bytes.size(),
.bytes = bytes,
.preloaded = image
};
}
ImageLocation FromWebDocument(const MTPWebDocument &document) {
return document.match([](const MTPDwebDocument &data) {
const auto size = GetSizeForDocument(data.vattributes().v);
// We don't use size from WebDocument, because it is not reliable.
// It can be > 0 and different from the real size
// that we get in upload.WebFile result.
//auto filesize = 0; // data.vsize().v;
return ImageLocation(
DownloadLocation{ WebFileLocation(
data.vurl().v,
data.vaccess_hash().v) },
size.width(),
size.height());
}, [](const MTPDwebDocumentNoProxy &data) {
const auto size = GetSizeForDocument(data.vattributes().v);
// We don't use size from WebDocument, because it is not reliable.
// It can be > 0 and different from the real size
// that we get in upload.WebFile result.
//auto filesize = 0; // data.vsize().v;
return ImageLocation(
DownloadLocation{ PlainUrlLocation{ qs(data.vurl()) } },
size.width(),
size.height());
});
}
} // namespace Images

View File

@ -0,0 +1,27 @@
/*
This file is part of Telegram Desktop,
the official desktop application for the Telegram messaging service.
For license and copyright information please follow this link:
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#pragma once
#include "ui/image/image_location.h"
namespace Main {
class Session;
} // namespace Main
namespace Images {
[[nodiscard]] ImageWithLocation FromPhotoSize(
not_null<Main::Session*> session,
const MTPDdocument &document,
const MTPPhotoSize &size);
[[nodiscard]] ImageWithLocation FromImageInMemory(
const QImage &image,
const char *format);
[[nodiscard]] ImageLocation FromWebDocument(const MTPWebDocument &document);
} // namespace Images