/* 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_source.h" #include "storage/file_download.h" #include "data/data_session.h" #include "storage/cache/storage_cache_database.h" #include "history/history_item.h" #include "history/history.h" #include "auth_session.h" namespace Images { ImageSource::ImageSource(QImage &&data, const QByteArray &format) : _data(std::move(data)) , _format(format) , _width(_data.width()) , _height(_data.height()) { } void ImageSource::load( Data::FileOrigin origin, bool loadFirst, bool prior) { if (_data.isNull() && !_bytes.isEmpty()) { _data = App::readImage(_bytes, &_format, false); } } void ImageSource::loadEvenCancelled( Data::FileOrigin origin, bool loadFirst, bool prior) { load(origin, loadFirst, prior); } QImage ImageSource::takeLoaded() { return _data; } void ImageSource::unload() { if (_bytes.isEmpty() && !_data.isNull()) { if (_format != "JPG") { _format = "PNG"; } { QBuffer buffer(&_bytes); _data.save(&buffer, _format); } Assert(!_bytes.isEmpty()); } _data = QImage(); } void ImageSource::automaticLoad( Data::FileOrigin origin, const HistoryItem *item) { } void ImageSource::automaticLoadSettingsChanged() { } bool ImageSource::loading() { return false; } bool ImageSource::displayLoading() { return false; } void ImageSource::cancel() { } float64 ImageSource::progress() { return 1.; } int ImageSource::loadOffset() { return 0; } const StorageImageLocation &ImageSource::location() { return StorageImageLocation::Null; } void ImageSource::refreshFileReference(const QByteArray &data) { } std::optional ImageSource::cacheKey() { return std::nullopt; } void ImageSource::setDelayedStorageLocation( const StorageImageLocation &location) { } void ImageSource::performDelayedLoad(Data::FileOrigin origin) { } bool ImageSource::isDelayedStorageImage() const { return false; } void ImageSource::setImageBytes(const QByteArray &bytes) { } int ImageSource::width() { return _width; } int ImageSource::height() { return _height; } int ImageSource::bytesSize() { return _bytes.size(); } void ImageSource::setInformation(int size, int width, int height) { if (width && height) { _width = width; _height = height; } } QByteArray ImageSource::bytesForCache() { auto result = QByteArray(); { QBuffer buffer(&result); if (!_data.save(&buffer, _format)) { if (_data.save(&buffer, "PNG")) { _format = "PNG"; } } } return result; } LocalFileSource::LocalFileSource( const QString &path, const QByteArray &content, const QByteArray &format, QImage &&data) : _path(path) , _bytes(content) , _format(format) , _data(std::move(data)) , _width(_data.width()) , _height(_data.height()) { } void LocalFileSource::load( Data::FileOrigin origin, bool loadFirst, bool prior) { if (!_data.isNull()) { return; } if (_bytes.isEmpty()) { QFile f(_path); if (f.size() <= App::kImageSizeLimit && f.open(QIODevice::ReadOnly)) { _bytes = f.readAll(); } if (_bytes.isEmpty()) { _bytes = "(bad)"; } } if (_bytes != "(bad)") { _data = App::readImage(_bytes, &_format, false, nullptr); } _width = std::max(_data.width(), 1); _height = std::max(_data.height(), 1); } void LocalFileSource::loadEvenCancelled( Data::FileOrigin origin, bool loadFirst, bool prior) { load(origin, loadFirst, prior); } QImage LocalFileSource::takeLoaded() { return std::move(_data); } void LocalFileSource::unload() { _data = QImage(); } void LocalFileSource::automaticLoad( Data::FileOrigin origin, const HistoryItem *item) { } void LocalFileSource::automaticLoadSettingsChanged() { } bool LocalFileSource::loading() { return false; } bool LocalFileSource::displayLoading() { return false; } void LocalFileSource::cancel() { } float64 LocalFileSource::progress() { return 1.; } int LocalFileSource::loadOffset() { return 0; } const StorageImageLocation &LocalFileSource::location() { return StorageImageLocation::Null; } void LocalFileSource::refreshFileReference(const QByteArray &data) { } std::optional LocalFileSource::cacheKey() { return std::nullopt; } void LocalFileSource::setDelayedStorageLocation( const StorageImageLocation &location) { } void LocalFileSource::performDelayedLoad(Data::FileOrigin origin) { } bool LocalFileSource::isDelayedStorageImage() const { return false; } void LocalFileSource::setImageBytes(const QByteArray &bytes) { _bytes = bytes; load({}, false, true); } int LocalFileSource::width() { ensureDimensionsKnown(); return _width; } int LocalFileSource::height() { ensureDimensionsKnown(); return _height; } int LocalFileSource::bytesSize() { ensureDimensionsKnown(); return _bytes.size(); } void LocalFileSource::setInformation(int size, int width, int height) { ensureDimensionsKnown(); // First load _bytes. if (width && height) { _width = width; _height = height; } } void LocalFileSource::ensureDimensionsKnown() { if (!_width || !_height) { load({}, false, false); } } QByteArray LocalFileSource::bytesForCache() { ensureDimensionsKnown(); return (_bytes == "(bad)") ? QByteArray() : _bytes; } QImage RemoteSource::takeLoaded() { if (!loaderValid() || !_loader->finished()) { return QImage(); } auto data = _loader->imageData(shrinkBox()); if (data.isNull()) { destroyLoader(CancelledFileLoader); return QImage(); } setInformation(_loader->bytes().size(), data.width(), data.height()); destroyLoader(); return data; } bool RemoteSource::loaderValid() const { return _loader && _loader != CancelledFileLoader; } void RemoteSource::destroyLoader(FileLoader *newValue) { Expects(loaderValid()); const auto loader = std::exchange(_loader, newValue); if (_loader == CancelledFileLoader) { loader->cancel(); } loader->stop(); delete loader; } void RemoteSource::loadLocal() { if (loaderValid()) { return; } _loader = createLoader(std::nullopt, LoadFromLocalOnly, true); if (_loader) _loader->start(); } void RemoteSource::setImageBytes(const QByteArray &bytes) { if (bytes.isEmpty()) { return; } else if (loaderValid()) { unload(); } _loader = createLoader({}, LoadFromLocalOnly, true); _loader->finishWithBytes(bytes); const auto location = this->location(); if (!location.isNull() && !bytes.isEmpty() && bytes.size() <= Storage::kMaxFileInMemory) { Auth().data().cache().putIfEmpty( Data::StorageCacheKey(location), Storage::Cache::Database::TaggedValue( base::duplicate(bytes), Data::kImageCacheTag)); } } bool RemoteSource::loading() { return loaderValid(); } void RemoteSource::automaticLoad( Data::FileOrigin origin, const HistoryItem *item) { if (_loader != CancelledFileLoader && item) { bool loadFromCloud = false; if (item->history()->peer->isUser()) { loadFromCloud = !(cAutoDownloadPhoto() & dbiadNoPrivate); } else { loadFromCloud = !(cAutoDownloadPhoto() & dbiadNoGroups); } if (_loader) { if (loadFromCloud) _loader->permitLoadFromCloud(); } else { _loader = createLoader( origin, loadFromCloud ? LoadFromCloudOrLocal : LoadFromLocalOnly, true); if (_loader) _loader->start(); } } } void RemoteSource::automaticLoadSettingsChanged() { if (_loader == CancelledFileLoader) { _loader = nullptr; } } void RemoteSource::load( Data::FileOrigin origin, bool loadFirst, bool prior) { if (!_loader) { _loader = createLoader(origin, LoadFromCloudOrLocal, false); } if (loaderValid()) { _loader->start(loadFirst, prior); } } void RemoteSource::loadEvenCancelled( Data::FileOrigin origin, bool loadFirst, bool prior) { if (_loader == CancelledFileLoader) { _loader = nullptr; } return load(origin, loadFirst, prior); } bool RemoteSource::displayLoading() { return loaderValid() && (!_loader->loadingLocal() || !_loader->autoLoading()); } void RemoteSource::cancel() { if (!loaderValid()) return; destroyLoader(CancelledFileLoader); } void RemoteSource::unload() { if (loaderValid()) { delete base::take(_loader); } } float64 RemoteSource::progress() { return loaderValid() ? _loader->currentProgress() : 0.; } int RemoteSource::loadOffset() { return loaderValid() ? _loader->currentOffset() : 0; } RemoteSource::~RemoteSource() { unload(); } const StorageImageLocation &RemoteSource::location() { return StorageImageLocation::Null; } void RemoteSource::refreshFileReference(const QByteArray &data) { } void RemoteSource::setDelayedStorageLocation( const StorageImageLocation &location) { } void RemoteSource::performDelayedLoad(Data::FileOrigin origin) { } bool RemoteSource::isDelayedStorageImage() const { return false; } QByteArray RemoteSource::bytesForCache() { return QByteArray(); } StorageSource::StorageSource(const StorageImageLocation &location, int size) : _location(location) , _size(size) { } void StorageSource::refreshFileReference(const QByteArray &data) { _location.refreshFileReference(data); } const StorageImageLocation &StorageSource::location() { return _location; } std::optional StorageSource::cacheKey() { return _location.isNull() ? std::nullopt : base::make_optional(Data::StorageCacheKey(_location)); } int StorageSource::width() { return _location.width(); } int StorageSource::height() { return _location.height(); } int StorageSource::bytesSize() { return _size; } void StorageSource::setInformation(int size, int width, int height) { if (size) { _size = size; } if (width && height) { _location.setSize(width, height); } } QSize StorageSource::shrinkBox() const { return QSize(); } FileLoader *StorageSource::createLoader( Data::FileOrigin origin, LoadFromCloudSetting fromCloud, bool autoLoading) { if (_location.isNull()) { return nullptr; } return new mtpFileLoader( &_location, origin, _size, fromCloud, autoLoading, Data::kImageCacheTag); } WebCachedSource::WebCachedSource( const WebFileLocation &location, QSize box, int size) : _location(location) , _box(box) , _size(size) { } WebCachedSource::WebCachedSource( const WebFileLocation &location, int width, int height, int size) : _location(location) , _width(width) , _height(height) , _size(size) { } std::optional WebCachedSource::cacheKey() { return _location.isNull() ? std::nullopt : base::make_optional(Data::WebDocumentCacheKey(_location)); } int WebCachedSource::width() { return _width; } int WebCachedSource::height() { return _height; } int WebCachedSource::bytesSize() { return _size; } void WebCachedSource::setInformation(int size, int width, int height) { if (size) { _size = size; } if (width && height) { _width = width; _height = height; } } QSize WebCachedSource::shrinkBox() const { return _box; } FileLoader *WebCachedSource::createLoader( Data::FileOrigin origin, LoadFromCloudSetting fromCloud, bool autoLoading) { return _location.isNull() ? nullptr : new mtpFileLoader( &_location, _size, fromCloud, autoLoading, Data::kImageCacheTag); } GeoPointSource::GeoPointSource(const GeoPointLocation &location) : _location(location) { } std::optional GeoPointSource::cacheKey() { return Data::GeoPointCacheKey(_location); } int GeoPointSource::width() { return _location.width * _location.scale; } int GeoPointSource::height() { return _location.height * _location.scale; } int GeoPointSource::bytesSize() { return _size; } void GeoPointSource::setInformation(int size, int width, int height) { Expects(_location.scale != 0); if (size) { _size = size; } if (width && height) { _location.width = width / _location.scale; _location.height = height / _location.scale; } } QSize GeoPointSource::shrinkBox() const { return QSize(); } FileLoader *GeoPointSource::createLoader( Data::FileOrigin origin, LoadFromCloudSetting fromCloud, bool autoLoading) { return new mtpFileLoader( &_location, _size, fromCloud, autoLoading, Data::kImageCacheTag); } DelayedStorageSource::DelayedStorageSource() : StorageSource(StorageImageLocation(), 0) { } DelayedStorageSource::DelayedStorageSource(int w, int h) : StorageSource(StorageImageLocation(w, h, 0, 0, 0, 0, {}), 0) { } void DelayedStorageSource::setDelayedStorageLocation( const StorageImageLocation &location) { _location = location; } void DelayedStorageSource::performDelayedLoad(Data::FileOrigin origin) { if (!_loadRequested) { return; } _loadRequested = false; if (_loadCancelled) { return; } if (base::take(_loadFromCloud)) { load(origin, false, true); } else { loadLocal(); } } void DelayedStorageSource::automaticLoad( Data::FileOrigin origin, const HistoryItem *item) { if (_location.isNull()) { if (!_loadCancelled && item) { bool loadFromCloud = false; if (item->history()->peer->isUser()) { loadFromCloud = !(cAutoDownloadPhoto() & dbiadNoPrivate); } else { loadFromCloud = !(cAutoDownloadPhoto() & dbiadNoGroups); } if (_loadRequested) { if (loadFromCloud) _loadFromCloud = loadFromCloud; } else { _loadFromCloud = loadFromCloud; _loadRequested = true; } } } else { StorageSource::automaticLoad(origin, item); } } void DelayedStorageSource::automaticLoadSettingsChanged() { if (_loadCancelled) _loadCancelled = false; StorageSource::automaticLoadSettingsChanged(); } void DelayedStorageSource::load( Data::FileOrigin origin, bool loadFirst, bool prior) { if (_location.isNull()) { _loadRequested = _loadFromCloud = true; } else { StorageSource::load(origin, loadFirst, prior); } } void DelayedStorageSource::loadEvenCancelled( Data::FileOrigin origin, bool loadFirst, bool prior) { _loadCancelled = false; StorageSource::loadEvenCancelled(origin, loadFirst, prior); } bool DelayedStorageSource::displayLoading() { return _location.isNull() ? true : StorageSource::displayLoading(); } void DelayedStorageSource::cancel() { if (_loadRequested) { _loadRequested = false; } StorageSource::cancel(); } bool DelayedStorageSource::isDelayedStorageImage() const { return true; } WebUrlSource::WebUrlSource(const QString &url, QSize box) : _url(url) , _box(box) { } WebUrlSource::WebUrlSource(const QString &url, int width, int height) : _url(url) , _width(width) , _height(height) { } std::optional WebUrlSource::cacheKey() { return Data::UrlCacheKey(_url); } int WebUrlSource::width() { return _width; } int WebUrlSource::height() { return _height; } int WebUrlSource::bytesSize() { return _size; } void WebUrlSource::setInformation(int size, int width, int height) { if (size) { _size = size; } if (width && height) { _width = width; _height = height; } } QSize WebUrlSource::shrinkBox() const { return _box; } FileLoader *WebUrlSource::createLoader( Data::FileOrigin origin, LoadFromCloudSetting fromCloud, bool autoLoading) { return new webFileLoader( _url, QString(), fromCloud, autoLoading, Data::kImageCacheTag); } } // namespace Images