From 31e3c6a2c61386b6a9adc94a918fbec6582badab Mon Sep 17 00:00:00 2001 From: John Preston Date: Sun, 5 Mar 2017 20:33:32 +0300 Subject: [PATCH] WebDocument wrap to HistoryPhoto supported. Only WebDocument with a valid 'size' field value and with a valid 'documentAttributeImageSize' attribute works wrapped as a photo. --- Telegram/SourceFiles/config.h | 2 - .../history/history_media_types.cpp | 25 ++- .../SourceFiles/storage/file_download.cpp | 156 ++++++++++++------ Telegram/SourceFiles/storage/file_download.h | 25 ++- Telegram/SourceFiles/ui/images.cpp | 39 +++++ Telegram/SourceFiles/ui/images.h | 80 ++++++++- 6 files changed, 255 insertions(+), 72 deletions(-) diff --git a/Telegram/SourceFiles/config.h b/Telegram/SourceFiles/config.h index 30d034e22..7577f1df9 100644 --- a/Telegram/SourceFiles/config.h +++ b/Telegram/SourceFiles/config.h @@ -340,8 +340,6 @@ enum { FileLoaderQueueStopTimeout = 5000, - DownloadPartSize = 64 * 1024, // 64kb for photo - DocumentDownloadPartSize = 128 * 1024, // 128kb for document UseBigFilesFrom = 10 * 1024 * 1024, // mtp big files methods used for files greater than 10mb MaxFileQueries = 16, // max 16 file parts downloaded at the same time MaxWebFileQueries = 8, // max 8 http[s] files downloaded at the same time diff --git a/Telegram/SourceFiles/history/history_media_types.cpp b/Telegram/SourceFiles/history/history_media_types.cpp index 4b039d33e..db8fb7c1b 100644 --- a/Telegram/SourceFiles/history/history_media_types.cpp +++ b/Telegram/SourceFiles/history/history_media_types.cpp @@ -3457,8 +3457,29 @@ QString HistoryInvoice::fillAmountAndCurrency(int amount, const QString ¤c void HistoryInvoice::fillFromData(const MTPDmessageMediaInvoice &data) { // init attach - if (data.has_photo()) { -// _attach = std::make_unique(_parent, data.vphoto.c_webDocument(), QString()); + if (data.has_photo() && data.vphoto.type() == mtpc_webDocument) { + auto &doc = data.vphoto.c_webDocument(); + auto imageSize = QSize(); + for (auto &attribute : doc.vattributes.v) { + if (attribute.type() == mtpc_documentAttributeImageSize) { + auto &size = attribute.c_documentAttributeImageSize(); + imageSize = QSize(size.vw.v, size.vh.v); + break; + } + } + if (!imageSize.isEmpty()) { + auto thumbsize = shrinkToKeepAspect(imageSize.width(), imageSize.height(), 100, 100); + auto thumb = ImagePtr(thumbsize.width(), thumbsize.height()); + + auto mediumsize = shrinkToKeepAspect(imageSize.width(), imageSize.height(), 320, 320); + auto medium = ImagePtr(mediumsize.width(), mediumsize.height()); + + auto full = ImagePtr(WebFileImageLocation(imageSize.width(), imageSize.height(), doc.vdc_id.v, doc.vurl.v, doc.vaccess_hash.v), doc.vsize.v); + auto photoId = rand_value(); + auto photo = App::photoSet(photoId, 0, 0, unixtime(), thumb, medium, full); + + _attach = std::make_unique(_parent, photo, QString()); + } } auto statusText = TextWithEntities { fillAmountAndCurrency(data.vtotal_amount.v, qs(data.vcurrency)), EntitiesInText() }; diff --git a/Telegram/SourceFiles/storage/file_download.cpp b/Telegram/SourceFiles/storage/file_download.cpp index ca048d7f8..e3563a7fb 100644 --- a/Telegram/SourceFiles/storage/file_download.cpp +++ b/Telegram/SourceFiles/storage/file_download.cpp @@ -45,6 +45,9 @@ struct DataRequested { }; QMap DataRequestedMap; +constexpr auto kDownloadPhotoPartSize = 64 * 1024; // 64kb for photo +constexpr auto kDownloadDocumentPartSize = 128 * 1024; // 128kb for document + } // namespace struct FileLoaderQueue { @@ -361,11 +364,11 @@ mtpFileLoader::mtpFileLoader(const StorageImageLocation *location, int32 size, L _queue = &i.value(); } -mtpFileLoader::mtpFileLoader(int32 dc, const uint64 &id, const uint64 &access, int32 version, LocationType type, const QString &to, int32 size, LoadToCacheSetting toCache, LoadFromCloudSetting fromCloud, bool autoLoading) +mtpFileLoader::mtpFileLoader(int32 dc, uint64 id, uint64 accessHash, int32 version, LocationType type, const QString &to, int32 size, LoadToCacheSetting toCache, LoadFromCloudSetting fromCloud, bool autoLoading) : FileLoader(to, size, type, toCache, fromCloud, autoLoading) , _dc(dc) , _id(id) -, _access(access) +, _accessHash(accessHash) , _version(version) { auto shiftedDcId = MTP::downloadDcId(_dc, 0); auto i = queues.find(shiftedDcId); @@ -375,6 +378,18 @@ mtpFileLoader::mtpFileLoader(int32 dc, const uint64 &id, const uint64 &access, i _queue = &i.value(); } +mtpFileLoader::mtpFileLoader(const WebFileImageLocation *location, int32 size, LoadFromCloudSetting fromCloud, bool autoLoading) +: FileLoader(QString(), size, UnknownFileLocation, LoadToCacheAsWell, fromCloud, autoLoading) +, _dc(location->dc()) +, _urlLocation(location) { + auto shiftedDcId = MTP::downloadDcId(_dc, 0); + auto i = queues.find(shiftedDcId); + if (i == queues.cend()) { + i = queues.insert(shiftedDcId, FileLoaderQueue(MaxFileQueries)); + } + _queue = &i.value(); +} + int32 mtpFileLoader::currentOffset(bool includeSkipped) const { return (_fileIsOpen ? _file.size() : _data.size()) - (includeSkipped ? 0 : _skippedBytes); } @@ -398,36 +413,24 @@ namespace { } bool mtpFileLoader::loadPart() { - if (_finished || _lastComplete || (!_requests.isEmpty() && !_size)) { + if (_finished || _lastComplete || (!_dcIndexByRequest.isEmpty() && !_size)) { if (DebugLogging::FileLoader() && _id) { - DEBUG_LOG(("FileLoader(%1): loadPart() returned, _finished=%2, _lastComplete=%3, _requests.size()=%4, _size=%5").arg(_id).arg(Logs::b(_finished)).arg(Logs::b(_lastComplete)).arg(_requests.size()).arg(_size)); + DEBUG_LOG(("FileLoader(%1): loadPart() returned, _finished=%2, _lastComplete=%3, _requests.size()=%4, _size=%5").arg(_id).arg(Logs::b(_finished)).arg(Logs::b(_lastComplete)).arg(_dcIndexByRequest.size()).arg(_size)); } return false; } if (_size && _nextRequestOffset >= _size) { if (DebugLogging::FileLoader() && _id) { - DEBUG_LOG(("FileLoader(%1): loadPart() returned, _size=%2, _nextRequestOffset=%3, _requests=%4").arg(_id).arg(_size).arg(_nextRequestOffset).arg(serializereqs(_requests))); + DEBUG_LOG(("FileLoader(%1): loadPart() returned, _size=%2, _nextRequestOffset=%3, _requests=%4").arg(_id).arg(_size).arg(_nextRequestOffset).arg(serializereqs(_dcIndexByRequest))); } return false; } - int32 limit = DocumentDownloadPartSize; - MTPInputFileLocation loc; - if (_location) { - loc = MTP_inputFileLocation(MTP_long(_location->volume()), MTP_int(_location->local()), MTP_long(_location->secret())); - limit = DownloadPartSize; - } else { - switch (_locationType) { - case VideoFileLocation: - case AudioFileLocation: - case DocumentFileLocation: loc = MTP_inputDocumentFileLocation(MTP_long(_id), MTP_long(_access), MTP_int(_version)); break; - default: cancel(true); return false; break; - } - } - int32 offset = _nextRequestOffset, dcIndex = 0; - DataRequested &dr(DataRequestedMap[_dc]); + auto offset = _nextRequestOffset; + auto dcIndex = 0; + auto &dr = DataRequestedMap[_dc]; if (_size) { - for (int32 i = 1; i < MTPDownloadSessionsCount; ++i) { + for (auto i = 1; i != MTPDownloadSessionsCount; ++i) { if (dr.v[i] < dr.v[dcIndex]) { dcIndex = i; } @@ -436,47 +439,86 @@ bool mtpFileLoader::loadPart() { App::app()->killDownloadSessionsStop(_dc); - mtpRequestId reqId = MTP::send(MTPupload_GetFile(loc, MTP_int(offset), MTP_int(limit)), rpcDone(&mtpFileLoader::partLoaded, offset), rpcFail(&mtpFileLoader::partFailed), MTP::downloadDcId(_dc, dcIndex), 50); - - ++_queue->queries; - dr.v[dcIndex] += limit; - _requests.insert(reqId, dcIndex); - _nextRequestOffset += limit; + auto requestId = makeRequest(offset, dcIndex); + _dcIndexByRequest.insert(requestId, dcIndex); if (DebugLogging::FileLoader() && _id) { - DEBUG_LOG(("FileLoader(%1): requested part with offset=%2, _queue->queries=%3, _nextRequestOffset=%4, _requests=%5").arg(_id).arg(offset).arg(_queue->queries).arg(_nextRequestOffset).arg(serializereqs(_requests))); + DEBUG_LOG(("FileLoader(%1): requested part with offset=%2, _queue->queries=%3, _nextRequestOffset=%4, _requests=%5").arg(_id).arg(offset).arg(_queue->queries).arg(_nextRequestOffset).arg(serializereqs(_dcIndexByRequest))); } return true; } -void mtpFileLoader::partLoaded(int32 offset, const MTPupload_File &result, mtpRequestId req) { - Requests::iterator i = _requests.find(req); - if (i == _requests.cend()) { - if (DebugLogging::FileLoader() && _id) { - DEBUG_LOG(("FileLoader(%1): request req=%2 for offset=%3 not found in _requests=%4").arg(_id).arg(req).arg(offset).arg(serializereqs(_requests))); - } - return loadNext(); +int mtpFileLoader::partSize() const { + if (_locationType == UnknownFileLocation) { + return kDownloadPhotoPartSize; } + return kDownloadDocumentPartSize; +} + +mtpRequestId mtpFileLoader::makeRequest(int offset, int dcIndex) { + auto limit = partSize(); + DataRequestedMap[_dc].v[dcIndex] += limit; + ++_queue->queries; + _nextRequestOffset += limit; + + if (_urlLocation) { + return MTP::send(MTPupload_GetWebFile(MTP_inputWebFileLocation(MTP_bytes(_urlLocation->url()), MTP_long(_urlLocation->accessHash())), MTP_int(offset), MTP_int(limit)), rpcDone(&mtpFileLoader::webPartLoaded, offset), rpcFail(&mtpFileLoader::partFailed), MTP::downloadDcId(_dc, dcIndex), 50); + } + MTPInputFileLocation loc; + if (_location) { + loc = MTP_inputFileLocation(MTP_long(_location->volume()), MTP_int(_location->local()), MTP_long(_location->secret())); + } else { + loc = MTP_inputDocumentFileLocation(MTP_long(_id), MTP_long(_accessHash), MTP_int(_version)); + } + return MTP::send(MTPupload_GetFile(loc, MTP_int(offset), MTP_int(limit)), rpcDone(&mtpFileLoader::normalPartLoaded, offset), rpcFail(&mtpFileLoader::partFailed), MTP::downloadDcId(_dc, dcIndex), 50); +} + +void mtpFileLoader::normalPartLoaded(int offset, const MTPupload_File &result, mtpRequestId req) { if (result.type() != mtpc_upload_file) { if (DebugLogging::FileLoader() && _id) { DEBUG_LOG(("FileLoader(%1): bad cons received! %2").arg(_id).arg(result.type())); } return cancel(true); } + auto bytes = gsl::as_bytes(gsl::make_span(result.c_upload_file().vbytes.v)); + return partLoaded(offset, bytes, req); +} - int32 limit = (_locationType == UnknownFileLocation) ? DownloadPartSize : DocumentDownloadPartSize; - int32 dcIndex = i.value(); +void mtpFileLoader::webPartLoaded(int offset, const MTPupload_WebFile &result, mtpRequestId req) { + if (result.type() != mtpc_upload_webFile) { + if (DebugLogging::FileLoader() && _id) { + DEBUG_LOG(("FileLoader(%1): bad cons received! %2").arg(_id).arg(result.type())); + } + return cancel(true); + } + auto &webFile = result.c_upload_webFile(); + if (webFile.vsize.v != _size) { + LOG(("MTP Error: Bad size provided by bot for webDocument: %1, real: %2").arg(_size).arg(webFile.vsize.v)); + return cancel(true); + } + auto bytes = gsl::as_bytes(gsl::make_span(webFile.vbytes.v)); + return partLoaded(offset, bytes, req); +} + +void mtpFileLoader::partLoaded(int offset, base::const_byte_span bytes, mtpRequestId req) { + auto i = _dcIndexByRequest.find(req); + if (i == _dcIndexByRequest.cend()) { + if (DebugLogging::FileLoader() && _id) { + DEBUG_LOG(("FileLoader(%1): request req=%2 for offset=%3 not found in _requests=%4").arg(_id).arg(req).arg(offset).arg(serializereqs(_dcIndexByRequest))); + } + return loadNext(); + } + + auto limit = partSize(); + auto dcIndex = i.value(); DataRequestedMap[_dc].v[dcIndex] -= limit; --_queue->queries; - _requests.erase(i); - - auto &d = result.c_upload_file(); - auto &bytes = d.vbytes.v; + _dcIndexByRequest.erase(i); if (DebugLogging::FileLoader() && _id) { - DEBUG_LOG(("FileLoader(%1): got part with offset=%2, bytes=%3, _queue->queries=%4, _nextRequestOffset=%5, _requests=%6").arg(_id).arg(offset).arg(bytes.size()).arg(_queue->queries).arg(_nextRequestOffset).arg(serializereqs(_requests))); + DEBUG_LOG(("FileLoader(%1): got part with offset=%2, bytes=%3, _queue->queries=%4, _nextRequestOffset=%5, _requests=%6").arg(_id).arg(offset).arg(bytes.size()).arg(_queue->queries).arg(_nextRequestOffset).arg(serializereqs(_dcIndexByRequest))); } if (bytes.size()) { @@ -488,7 +530,7 @@ void mtpFileLoader::partLoaded(int32 offset, const MTPupload_File &result, mtpRe _skippedBytes += offset - fsize; } _file.seek(offset); - if (_file.write(bytes.data(), bytes.size()) != qint64(bytes.size())) { + if (_file.write(reinterpret_cast(bytes.data()), bytes.size()) != qint64(bytes.size())) { return cancel(true); } } else { @@ -498,20 +540,22 @@ void mtpFileLoader::partLoaded(int32 offset, const MTPupload_File &result, mtpRe _data.resize(offset); } if (offset == _data.size()) { - _data.append(bytes.data(), bytes.size()); + _data.append(reinterpret_cast(bytes.data()), bytes.size()); } else { _skippedBytes -= bytes.size(); if (int64(offset + bytes.size()) > _data.size()) { _data.resize(offset + bytes.size()); } - memcpy(_data.data() + offset, bytes.data(), bytes.size()); + auto src = bytes; + auto dst = gsl::make_span(_data).subspan(offset, bytes.size()); + base::copy_bytes(gsl::as_writeable_bytes(dst), src); } } } if (!bytes.size() || (bytes.size() % 1024)) { // bad next offset _lastComplete = true; } - if (_requests.isEmpty() && (_lastComplete || (_size && _nextRequestOffset >= _size))) { + if (_dcIndexByRequest.isEmpty() && (_lastComplete || (_size && _nextRequestOffset >= _size))) { if (!_fname.isEmpty() && (_toCache == LoadToCacheAsWell)) { if (!_fileIsOpen) _fileIsOpen = _file.open(QIODevice::WriteOnly); if (!_fileIsOpen) { @@ -534,7 +578,9 @@ void mtpFileLoader::partLoaded(int32 offset, const MTPupload_File &result, mtpRe } if (_localStatus == LocalNotFound || _localStatus == LocalFailed) { - if (_locationType != UnknownFileLocation) { // audio, video, document + if (_urlLocation) { + Local::writeImage(storageKey(*_urlLocation), StorageImageSaved(_data)); + } else if (_locationType != UnknownFileLocation) { // audio, video, document MediaKey mkey = mediaKey(_locationType, _dc, _id, _version); if (!_fname.isEmpty()) { Local::writeFileLocation(mkey, FileLocation(_fname)); @@ -552,7 +598,7 @@ void mtpFileLoader::partLoaded(int32 offset, const MTPupload_File &result, mtpRe } } else { if (DebugLogging::FileLoader() && _id) { - DEBUG_LOG(("FileLoader(%1): not done yet, _lastComplete=%2, _size=%3, _nextRequestOffset=%4, _requests=%5").arg(_id).arg(Logs::b(_lastComplete)).arg(_size).arg(_nextRequestOffset).arg(serializereqs(_requests))); + DEBUG_LOG(("FileLoader(%1): not done yet, _lastComplete=%2, _size=%3, _nextRequestOffset=%4, _requests=%5").arg(_id).arg(Logs::b(_lastComplete)).arg(_size).arg(_nextRequestOffset).arg(serializereqs(_dcIndexByRequest))); } } if (_finished) { @@ -572,17 +618,17 @@ bool mtpFileLoader::partFailed(const RPCError &error) { } void mtpFileLoader::cancelRequests() { - if (_requests.isEmpty()) return; + if (_dcIndexByRequest.isEmpty()) return; - int32 limit = (_locationType == UnknownFileLocation) ? DownloadPartSize : DocumentDownloadPartSize; + auto limit = partSize(); DataRequested &dr(DataRequestedMap[_dc]); - for (Requests::const_iterator i = _requests.cbegin(), e = _requests.cend(); i != e; ++i) { + for (auto i = _dcIndexByRequest.cbegin(), e = _dcIndexByRequest.cend(); i != e; ++i) { MTP::cancel(i.key()); int32 dcIndex = i.value(); dr.v[dcIndex] -= limit; } - _queue->queries -= _requests.size(); - _requests.clear(); + _queue->queries -= _dcIndexByRequest.size(); + _dcIndexByRequest.clear(); if (!_queue->queries) { Messenger::Instance().killDownloadSessionsStart(_dc); @@ -597,7 +643,9 @@ bool mtpFileLoader::tryLoadLocal() { return true; } - if (_location) { + if (_urlLocation) { + _localTaskId = Local::startImageLoad(storageKey(*_urlLocation), this); + } else if (_location) { _localTaskId = Local::startImageLoad(storageKey(*_location), this); } else { if (_toCache == LoadToCacheAsWell) { diff --git a/Telegram/SourceFiles/storage/file_download.h b/Telegram/SourceFiles/storage/file_download.h index 783bbeef5..fa5fab9d1 100644 --- a/Telegram/SourceFiles/storage/file_download.h +++ b/Telegram/SourceFiles/storage/file_download.h @@ -169,12 +169,14 @@ protected: }; class StorageImageLocation; +class WebFileImageLocation; class mtpFileLoader : public FileLoader, public RPCSender { Q_OBJECT public: mtpFileLoader(const StorageImageLocation *location, int32 size, LoadFromCloudSetting fromCloud, bool autoLoading); - mtpFileLoader(int32 dc, const uint64 &id, const uint64 &access, int32 version, LocationType type, const QString &toFile, int32 size, LoadToCacheSetting toCache, LoadFromCloudSetting fromCloud, bool autoLoading); + mtpFileLoader(int32 dc, uint64 id, uint64 accessHash, int32 version, LocationType type, const QString &toFile, int32 size, LoadToCacheSetting toCache, LoadFromCloudSetting fromCloud, bool autoLoading); + mtpFileLoader(const WebFileImageLocation *location, int32 size, LoadFromCloudSetting fromCloud, bool autoLoading); int32 currentOffset(bool includeSkipped = false) const override; @@ -188,28 +190,35 @@ public: ~mtpFileLoader(); -protected: +private: bool tryLoadLocal() override; void cancelRequests() override; - typedef QMap Requests; - Requests _requests; + int partSize() const; + mtpRequestId makeRequest(int offset, int dcIndex); + + QMap _dcIndexByRequest; bool loadPart() override; - void partLoaded(int32 offset, const MTPupload_File &result, mtpRequestId req); + void normalPartLoaded(int offset, const MTPupload_File &result, mtpRequestId req); + void webPartLoaded(int offset, const MTPupload_WebFile &result, mtpRequestId req); + + void partLoaded(int offset, base::const_byte_span bytes, mtpRequestId req); bool partFailed(const RPCError &error); bool _lastComplete = false; int32 _skippedBytes = 0; int32 _nextRequestOffset = 0; - int32 _dc; + int32 _dc; // for photo locations const StorageImageLocation *_location = nullptr; - uint64 _id = 0; // for other locations - uint64 _access = 0; + uint64 _id = 0; // for document locations + uint64 _accessHash = 0; int32 _version = 0; + const WebFileImageLocation *_urlLocation = nullptr; // for webdocument locations + }; class webFileLoaderPrivate; diff --git a/Telegram/SourceFiles/ui/images.cpp b/Telegram/SourceFiles/ui/images.cpp index b255a05b1..d4a9e70e7 100644 --- a/Telegram/SourceFiles/ui/images.cpp +++ b/Telegram/SourceFiles/ui/images.cpp @@ -364,6 +364,9 @@ Image *blank() { using StorageImages = QMap; StorageImages storageImages; +using WebFileImages = QMap; +WebFileImages webFileImages; + int64 globalAcquiredSize = 0; uint64 PixKey(int width, int height, Images::Options options) { @@ -377,6 +380,7 @@ uint64 SinglePixKey(Images::Options options) { } // namespace StorageImageLocation StorageImageLocation::Null; +WebFileImageLocation WebFileImageLocation::Null; bool Image::isNull() const { return (this == blank()); @@ -809,6 +813,9 @@ void clearStorageImages() { for (auto image : base::take(webImages)) { delete image; } + for (auto image : base::take(webFileImages)) { + delete image; + } } void clearAllImages() { @@ -995,6 +1002,29 @@ FileLoader *StorageImage::createLoader(LoadFromCloudSetting fromCloud, bool auto return new mtpFileLoader(&_location, _size, fromCloud, autoLoading); } +WebFileImage::WebFileImage(const WebFileImageLocation &location, int32 size) +: _location(location) +, _size(size) { +} + +int32 WebFileImage::countWidth() const { + return _location.width(); +} + +int32 WebFileImage::countHeight() const { + return _location.height(); +} + +void WebFileImage::setInformation(int32 size, int32 width, int32 height) { + _size = size; + _location.setSize(width, height); +} + +FileLoader *WebFileImage::createLoader(LoadFromCloudSetting fromCloud, bool autoLoading) { + if (_location.isNull()) return 0; + return new mtpFileLoader(&_location, _size, fromCloud, autoLoading); +} + DelayedStorageImage::DelayedStorageImage() : StorageImage(StorageImageLocation()) , _loadRequested(false) , _loadCancelled(false) @@ -1188,6 +1218,15 @@ StorageImage *getImage(const StorageImageLocation &location, const QByteArray &b return i.value(); } +WebFileImage *getImage(const WebFileImageLocation &location, int32 size) { + auto key = storageKey(location); + auto i = webFileImages.constFind(key); + if (i == webFileImages.cend()) { + i = webFileImages.insert(key, new WebFileImage(location, size)); + } + return i.value(); +} + } // namespace internal ReadAccessEnabler::ReadAccessEnabler(const PsFileBookmark *bookmark) : _bookmark(bookmark), _failed(_bookmark ? !_bookmark->enable() : false) { diff --git a/Telegram/SourceFiles/ui/images.h b/Telegram/SourceFiles/ui/images.h index f8f5e31ae..25deaf813 100644 --- a/Telegram/SourceFiles/ui/images.h +++ b/Telegram/SourceFiles/ui/images.h @@ -82,8 +82,7 @@ inline int32 unpackIntSecond(uint64 v) { class StorageImageLocation { public: - StorageImageLocation() : _widthheight(0), _dclocal(0), _volume(0), _secret(0) { - } + StorageImageLocation() = default; StorageImageLocation(int32 width, int32 height, int32 dc, const uint64 &volume, int32 local, const uint64 &secret) : _widthheight(packIntInt(width, height)), _dclocal(packIntInt(dc, local)), _volume(volume), _secret(secret) { } StorageImageLocation(int32 width, int32 height, const MTPDfileLocation &location) : _widthheight(packIntInt(width, height)), _dclocal(packIntInt(location.vdc_id.v, location.vlocal_id.v)), _volume(location.vvolume_id.v), _secret(location.vsecret.v) { @@ -116,10 +115,10 @@ public: static StorageImageLocation Null; private: - uint64 _widthheight; - uint64 _dclocal; - uint64 _volume; - uint64 _secret; + uint64 _widthheight = 0; + uint64 _dclocal = 0; + uint64 _volume = 0; + uint64 _secret = 0; friend inline bool operator==(const StorageImageLocation &a, const StorageImageLocation &b) { return (a._dclocal == b._dclocal) && (a._volume == b._volume) && (a._secret == b._secret); @@ -131,6 +130,51 @@ inline bool operator!=(const StorageImageLocation &a, const StorageImageLocation return !(a == b); } +class WebFileImageLocation { +public: + WebFileImageLocation() = default; + WebFileImageLocation(int32 width, int32 height, int32 dc, const QByteArray &url, uint64 accessHash) : _widthheight(packIntInt(width, height)), _accessHash(accessHash), _url(url), _dc(dc) { + } + bool isNull() const { + return !_dc; + } + int32 width() const { + return unpackIntFirst(_widthheight); + } + int32 height() const { + return unpackIntSecond(_widthheight); + } + void setSize(int32 width, int32 height) { + _widthheight = packIntInt(width, height); + } + int32 dc() const { + return _dc; + } + uint64 accessHash() const { + return _accessHash; + } + const QByteArray &url() const { + return _url; + } + + static WebFileImageLocation Null; + +private: + uint64 _widthheight = 0; + uint64 _accessHash = 0; + QByteArray _url; + int32 _dc = 0; + + friend inline bool operator==(const WebFileImageLocation &a, const WebFileImageLocation &b) { + return (a._dc == b._dc) && (a._accessHash == b._accessHash) && (a._url == b._url); + } + +}; + +inline bool operator!=(const WebFileImageLocation &a, const WebFileImageLocation &b) { + return !(a == b); +} + namespace Images { QImage prepareBlur(QImage image); @@ -290,6 +334,11 @@ inline StorageKey storageKey(const MTPDfileLocation &location) { inline StorageKey storageKey(const StorageImageLocation &location) { return storageKey(location.dc(), location.volume(), location.local()); } +inline StorageKey storageKey(const WebFileImageLocation &location) { + auto url = location.url(); + auto sha = hashSha1(url.data(), url.size()); + return storageKey(location.dc(), *reinterpret_cast(sha.data()), *reinterpret_cast(sha.data() + sizeof(uint64))); +} class RemoteImage : public Image { public: @@ -356,6 +405,22 @@ protected: }; +class WebFileImage : public RemoteImage { +public: + WebFileImage(const WebFileImageLocation &location, int32 size = 0); + +protected: + void setInformation(int32 size, int32 width, int32 height) override; + FileLoader *createLoader(LoadFromCloudSetting fromCloud, bool autoLoading) override; + + WebFileImageLocation _location; + int32 _size; + + int32 countWidth() const override; + int32 countHeight() const override; + +}; + class DelayedStorageImage : public StorageImage { public: @@ -426,6 +491,7 @@ namespace internal { Image *getImage(int32 width, int32 height); StorageImage *getImage(const StorageImageLocation &location, int32 size = 0); StorageImage *getImage(const StorageImageLocation &location, const QByteArray &bytes); + WebFileImage *getImage(const WebFileImageLocation &location, int32 size = 0); } // namespace internal class ImagePtr : public ManagedPtr { @@ -447,6 +513,8 @@ public: } ImagePtr(const StorageImageLocation &location, const QByteArray &bytes) : Parent(internal::getImage(location, bytes)) { } + ImagePtr(const WebFileImageLocation &location, int32 size = 0) : Parent(internal::getImage(location, size)) { + } ImagePtr(int32 width, int32 height, const MTPFileLocation &location, ImagePtr def = ImagePtr()); ImagePtr(int32 width, int32 height) : Parent(internal::getImage(width, height)) { }