From 4b5b79e4153eb68a17ed87206615f58eb95d3921 Mon Sep 17 00:00:00 2001 From: John Preston Date: Fri, 12 Oct 2018 19:41:51 +0300 Subject: [PATCH] Use final Image with different Images::Source-s. --- Telegram/SourceFiles/data/data_document.cpp | 6 +- .../SourceFiles/data/data_media_types.cpp | 7 +- Telegram/SourceFiles/data/data_peer.cpp | 6 +- Telegram/SourceFiles/data/data_photo.cpp | 3 +- Telegram/SourceFiles/data/data_session.cpp | 16 +- Telegram/SourceFiles/data/data_session.h | 2 +- Telegram/SourceFiles/data/data_types.h | 2 +- .../SourceFiles/storage/file_download.cpp | 66 +- Telegram/SourceFiles/storage/file_download.h | 9 +- Telegram/SourceFiles/storage/file_upload.cpp | 8 +- .../SourceFiles/storage/localimageloader.cpp | 43 +- .../SourceFiles/storage/localimageloader.h | 2 +- Telegram/SourceFiles/ui/image.cpp | 1480 ++++++++++------- Telegram/SourceFiles/ui/image.h | 707 ++++---- Telegram/SourceFiles/ui/images.cpp | 8 +- Telegram/SourceFiles/ui/images.h | 4 +- 16 files changed, 1376 insertions(+), 993 deletions(-) diff --git a/Telegram/SourceFiles/data/data_document.cpp b/Telegram/SourceFiles/data/data_document.cpp index bf00dccdd..335923dcf 100644 --- a/Telegram/SourceFiles/data/data_document.cpp +++ b/Telegram/SourceFiles/data/data_document.cpp @@ -660,8 +660,8 @@ bool DocumentData::loaded(FilePathResolveType type) const { auto that = const_cast(this); that->_location = FileLocation(_loader->fileName()); that->_data = _loader->bytes(); - if (that->sticker() && !_loader->imagePixmap().isNull()) { - that->sticker()->img = ImagePtr(_data, _loader->imageFormat(), _loader->imagePixmap()); + if (that->sticker() && !_loader->imageData().isNull()) { + that->sticker()->img = ImagePtr(_data, _loader->imageFormat(), _loader->imageData()); } destroyLoaderDelayed(); } @@ -954,7 +954,7 @@ ImagePtr DocumentData::makeReplyPreview(Data::FileOrigin origin) { auto options = Images::Option::Smooth | (isVideoMessage() ? Images::Option::Circled : Images::Option::None) | Images::Option::TransparentBackground; auto outerSize = st::msgReplyBarSize.height(); auto image = thumb->pixNoCache(origin, thumbSize.width(), thumbSize.height(), options, outerSize, outerSize); - replyPreview = ImagePtr(image, "PNG"); + replyPreview = ImagePtr(image.toImage(), "PNG"); } else { thumb->load(origin); } diff --git a/Telegram/SourceFiles/data/data_media_types.cpp b/Telegram/SourceFiles/data/data_media_types.cpp index e65532e6f..ec3711b52 100644 --- a/Telegram/SourceFiles/data/data_media_types.cpp +++ b/Telegram/SourceFiles/data/data_media_types.cpp @@ -354,15 +354,14 @@ bool MediaPhoto::updateSentMedia(const MTPMessageMedia &media) { if (key.isNull() || image->isNull() || !image->loaded()) { return; } - if (image->savedData().isEmpty()) { - image->forget(); - } else if (image->savedData().size() > Storage::kMaxFileInMemory) { + auto bytes = image->bytesForCache(); + if (bytes.isEmpty() || bytes.size() > Storage::kMaxFileInMemory) { return; } Auth().data().cache().putIfEmpty( Data::StorageCacheKey(key), Storage::Cache::Database::TaggedValue( - image->savedData(), + std::move(bytes), Data::kImageCacheTag)); }; auto &sizes = photo.c_photo().vsizes.v; diff --git a/Telegram/SourceFiles/data/data_peer.cpp b/Telegram/SourceFiles/data/data_peer.cpp index 735d4c378..2ef222cd6 100644 --- a/Telegram/SourceFiles/data/data_peer.cpp +++ b/Telegram/SourceFiles/data/data_peer.cpp @@ -195,6 +195,9 @@ ImagePtr PeerData::currentUserpic() const { return _userpic; } } + if (!_userpicEmpty) { + refreshEmptyUserpic(); + } return ImagePtr(); } @@ -296,10 +299,9 @@ void PeerData::clearUserpic() { auto image = Messenger::Instance().logoNoMargin().scaledToWidth( kUserpicSize, Qt::SmoothTransformation); - auto pixmap = App::pixmapFromImageInPlace(std::move(image)); return _userpic ? _userpic - : ImagePtr(std::move(pixmap), "PNG"); + : ImagePtr(std::move(image), "PNG"); } return ImagePtr(); }(); diff --git a/Telegram/SourceFiles/data/data_photo.cpp b/Telegram/SourceFiles/data/data_photo.cpp index f1e7e5321..41f6d116e 100644 --- a/Telegram/SourceFiles/data/data_photo.cpp +++ b/Telegram/SourceFiles/data/data_photo.cpp @@ -126,7 +126,8 @@ ImagePtr PhotoData::makeReplyPreview(Data::FileOrigin origin) { origin, w * st::msgReplyBarSize.height() / h, st::msgReplyBarSize.height()) - : image->pix(origin, st::msgReplyBarSize.height())), + : image->pix(origin, st::msgReplyBarSize.height()) + ).toImage(), "PNG"); }; if (thumb->isDelayedStorageImage() diff --git a/Telegram/SourceFiles/data/data_session.cpp b/Telegram/SourceFiles/data/data_session.cpp index 6307db471..b587ac220 100644 --- a/Telegram/SourceFiles/data/data_session.cpp +++ b/Telegram/SourceFiles/data/data_session.cpp @@ -777,9 +777,9 @@ not_null Session::photo(const MTPDphoto &data) { not_null Session::photo( const MTPPhoto &data, const PreparedPhotoThumbs &thumbs) { - auto thumb = (const QPixmap*)nullptr; - auto medium = (const QPixmap*)nullptr; - auto full = (const QPixmap*)nullptr; + auto thumb = (const QImage*)nullptr; + auto medium = (const QImage*)nullptr; + auto full = (const QImage*)nullptr; auto thumbLevel = -1; auto mediumLevel = -1; auto fullLevel = -1; @@ -813,9 +813,9 @@ not_null Session::photo( data.c_photo().vaccess_hash.v, data.c_photo().vfile_reference.v, data.c_photo().vdate.v, - ImagePtr(*thumb, "JPG"), - ImagePtr(*medium, "JPG"), - ImagePtr(*full, "JPG")); + ImagePtr(base::duplicate(*thumb), "JPG"), + ImagePtr(base::duplicate(*medium), "JPG"), + ImagePtr(base::duplicate(*full), "JPG")); case mtpc_photoEmpty: return photo(data.c_photoEmpty().vid.v); @@ -1013,7 +1013,7 @@ not_null Session::document(const MTPDdocument &data) { not_null Session::document( const MTPdocument &data, - const QPixmap &thumb) { + QImage &&thumb) { switch (data.type()) { case mtpc_documentEmpty: return document(data.c_documentEmpty().vid.v); @@ -1027,7 +1027,7 @@ not_null Session::document( fields.vdate.v, fields.vattributes.v, qs(fields.vmime_type), - ImagePtr(thumb, "JPG"), + ImagePtr(std::move(thumb), "JPG"), fields.vdc_id.v, fields.vsize.v, StorageImageLocation()); diff --git a/Telegram/SourceFiles/data/data_session.h b/Telegram/SourceFiles/data/data_session.h index 2f9d8a89d..62b60aa68 100644 --- a/Telegram/SourceFiles/data/data_session.h +++ b/Telegram/SourceFiles/data/data_session.h @@ -269,7 +269,7 @@ public: not_null document(const MTPDdocument &data); not_null document( const MTPdocument &data, - const QPixmap &thumb); + QImage &&thumb); not_null document( DocumentId id, const uint64 &access, diff --git a/Telegram/SourceFiles/data/data_types.h b/Telegram/SourceFiles/data/data_types.h index 9766fb824..45bb702a8 100644 --- a/Telegram/SourceFiles/data/data_types.h +++ b/Telegram/SourceFiles/data/data_types.h @@ -294,7 +294,7 @@ using WebPageId = uint64; using GameId = uint64; constexpr auto CancelledWebPageId = WebPageId(0xFFFFFFFFFFFFFFFFULL); -using PreparedPhotoThumbs = QMap; +using PreparedPhotoThumbs = QMap; // [0] == -1 -- counting, [0] == -2 -- could not count using VoiceWaveform = QVector; diff --git a/Telegram/SourceFiles/storage/file_download.cpp b/Telegram/SourceFiles/storage/file_download.cpp index 8203ed2da..ee8c0c03d 100644 --- a/Telegram/SourceFiles/storage/file_download.cpp +++ b/Telegram/SourceFiles/storage/file_download.cpp @@ -127,6 +127,31 @@ FileLoader::FileLoader( Expects(!_filename.isEmpty() || (_size <= Storage::kMaxFileInMemory)); } +void FileLoader::finishWithBytes(const QByteArray &data) { + _data = data; + _localStatus = LocalStatus::Loaded; + if (!_filename.isEmpty() && _toCache == LoadToCacheAsWell) { + if (!_fileIsOpen) _fileIsOpen = _file.open(QIODevice::WriteOnly); + if (!_fileIsOpen) { + cancel(true); + return; + } + if (_file.write(_data) != qint64(_data.size())) { + cancel(true); + return; + } + } + + _finished = true; + if (_fileIsOpen) { + _file.close(); + _fileIsOpen = false; + Platform::File::PostprocessDownloaded( + QFileInfo(_file).absoluteFilePath()); + } + _downloader->taskFinished().notify(); +} + QByteArray FileLoader::imageFormat(const QSize &shrinkBox) const { if (_imageFormat.isEmpty() && _locationType == UnknownFileLocation) { readImage(shrinkBox); @@ -134,11 +159,11 @@ QByteArray FileLoader::imageFormat(const QSize &shrinkBox) const { return _imageFormat; } -QPixmap FileLoader::imagePixmap(const QSize &shrinkBox) const { - if (_imagePixmap.isNull() && _locationType == UnknownFileLocation) { +QImage FileLoader::imageData(const QSize &shrinkBox) const { + if (_imageData.isNull() && _locationType == UnknownFileLocation) { readImage(shrinkBox); } - return _imagePixmap; + return _imageData; } void FileLoader::readImage(const QSize &shrinkBox) const { @@ -146,9 +171,9 @@ void FileLoader::readImage(const QSize &shrinkBox) const { auto image = App::readImage(_data, &format, false); if (!image.isNull()) { if (!shrinkBox.isEmpty() && (image.width() > shrinkBox.width() || image.height() > shrinkBox.height())) { - _imagePixmap = App::pixmapFromImageInPlace(image.scaled(shrinkBox, Qt::KeepAspectRatio, Qt::SmoothTransformation)); + _imageData = image.scaled(shrinkBox, Qt::KeepAspectRatio, Qt::SmoothTransformation); } else { - _imagePixmap = App::pixmapFromImageInPlace(std::move(image)); + _imageData = std::move(image); } _imageFormat = format; } @@ -222,39 +247,18 @@ FileLoader::~FileLoader() { void FileLoader::localLoaded( const StorageImageSaved &result, const QByteArray &imageFormat, - const QPixmap &imagePixmap) { + const QImage &imageData) { _localLoading.kill(); if (result.data.isEmpty()) { _localStatus = LocalStatus::NotFound; start(true); return; } - _data = result.data; - if (!imagePixmap.isNull()) { + if (!imageData.isNull()) { _imageFormat = imageFormat; - _imagePixmap = imagePixmap; + _imageData = imageData; } - _localStatus = LocalStatus::Loaded; - if (!_filename.isEmpty() && _toCache == LoadToCacheAsWell) { - if (!_fileIsOpen) _fileIsOpen = _file.open(QIODevice::WriteOnly); - if (!_fileIsOpen) { - cancel(true); - return; - } - if (_file.write(_data) != qint64(_data.size())) { - cancel(true); - return; - } - } - - _finished = true; - if (_fileIsOpen) { - _file.close(); - _fileIsOpen = false; - Platform::File::PostprocessDownloaded( - QFileInfo(_file).absoluteFilePath()); - } - _downloader->taskFinished().notify(); + finishWithBytes(result.data); emit progress(this); @@ -389,7 +393,7 @@ void FileLoader::loadLocal(const Storage::Cache::Key &key) { localLoaded( StorageImageSaved(std::move(value)), format, - App::pixmapFromImageInPlace(std::move(image))); + std::move(image)); }); }; Auth().data().cache().get(key, [=, callback = std::move(done)]( diff --git a/Telegram/SourceFiles/storage/file_download.h b/Telegram/SourceFiles/storage/file_download.h index 98756e9db..f6befa667 100644 --- a/Telegram/SourceFiles/storage/file_download.h +++ b/Telegram/SourceFiles/storage/file_download.h @@ -84,6 +84,7 @@ public: bool finished() const { return _finished; } + void finishWithBytes(const QByteArray &data); bool cancelled() const { return _cancelled; } @@ -94,7 +95,7 @@ public: return 0; } QByteArray imageFormat(const QSize &shrinkBox = QSize()) const; - QPixmap imagePixmap(const QSize &shrinkBox = QSize()) const; + QImage imageData(const QSize &shrinkBox = QSize()) const; QString fileName() const { return _filename; } @@ -134,8 +135,8 @@ public: void localLoaded( const StorageImageSaved &result, - const QByteArray &imageFormat = QByteArray(), - const QPixmap &imagePixmap = QPixmap()); + const QByteArray &imageFormat, + const QImage &imageData); signals: void progress(FileLoader *loader); @@ -191,7 +192,7 @@ protected: base::binary_guard _localLoading; mutable QByteArray _imageFormat; - mutable QPixmap _imagePixmap; + mutable QImage _imageData; }; diff --git a/Telegram/SourceFiles/storage/file_upload.cpp b/Telegram/SourceFiles/storage/file_upload.cpp index 9fed8a93a..1d6b9b90f 100644 --- a/Telegram/SourceFiles/storage/file_upload.cpp +++ b/Telegram/SourceFiles/storage/file_upload.cpp @@ -124,7 +124,9 @@ void Uploader::uploadMedia(const FullMsgId &msgId, const SendMediaReady &media) } else if (media.type == SendMediaType::File || media.type == SendMediaType::Audio) { const auto document = media.photoThumbs.isEmpty() ? Auth().data().document(media.document) - : Auth().data().document(media.document, media.photoThumbs.begin().value()); + : Auth().data().document( + media.document, + base::duplicate(media.photoThumbs.begin().value())); if (!media.data.isEmpty()) { document->setData(media.data); if (document->saveToCache() @@ -153,7 +155,9 @@ void Uploader::upload( } else if (file->type == SendMediaType::File || file->type == SendMediaType::Audio) { auto document = file->thumb.isNull() ? Auth().data().document(file->document) - : Auth().data().document(file->document, file->thumb); + : Auth().data().document( + file->document, + base::duplicate(file->thumb)); document->uploadingData = std::make_unique(document->size); if (!file->content.isEmpty()) { document->setData(file->content); diff --git a/Telegram/SourceFiles/storage/localimageloader.cpp b/Telegram/SourceFiles/storage/localimageloader.cpp index 7fd8d7321..c89c5657f 100644 --- a/Telegram/SourceFiles/storage/localimageloader.cpp +++ b/Telegram/SourceFiles/storage/localimageloader.cpp @@ -31,26 +31,26 @@ SendMediaReady PreparePeerPhoto(PeerId peerId, QImage &&image) { image.save(&jpegBuffer, "JPG", 87); const auto scaled = [&](int size) { - return App::pixmapFromImageInPlace(image.scaled( + return image.scaled( size, size, Qt::KeepAspectRatio, - Qt::SmoothTransformation)); + Qt::SmoothTransformation); }; - const auto push = [&](const char *type, QPixmap &&pixmap) { + const auto push = [&](const char *type, QImage &&image) { photoSizes.push_back(MTP_photoSize( MTP_string(type), MTP_fileLocationUnavailable( MTP_long(0), MTP_int(0), MTP_long(0)), - MTP_int(pixmap.width()), - MTP_int(pixmap.height()), MTP_int(0))); - photoThumbs.insert(type[0], std::move(pixmap)); + MTP_int(image.width()), + MTP_int(image.height()), MTP_int(0))); + photoThumbs.insert(type[0], std::move(image)); }; push("a", scaled(160)); push("b", scaled(320)); - push("c", App::pixmapFromImageInPlace(std::move(image))); + push("c", std::move(image)); const auto id = rand_value(); const auto photo = MTP_photo( @@ -531,7 +531,7 @@ void FileLoadTask::process() { PreparedPhotoThumbs photoThumbs; QVector photoSizes; - QPixmap thumb; + QImage thumb; QVector attributes(1, MTP_documentAttributeFilename(MTP_string(filename))); @@ -552,7 +552,7 @@ void FileLoadTask::process() { if (!song->cover.isNull()) { // cover to thumb auto coverWidth = song->cover.width(); auto coverHeight = song->cover.height(); - auto full = (coverWidth > 90 || coverHeight > 90) ? App::pixmapFromImageInPlace(song->cover.scaled(90, 90, Qt::KeepAspectRatio, Qt::SmoothTransformation)) : App::pixmapFromImageInPlace(std::move(song->cover)); + auto full = (coverWidth > 90 || coverHeight > 90) ? song->cover.scaled(90, 90, Qt::KeepAspectRatio, Qt::SmoothTransformation) : std::move(song->cover); { auto thumbFormat = QByteArray("JPG"); auto thumbQuality = 87; @@ -588,10 +588,9 @@ void FileLoadTask::process() { cover.save(&buffer, thumbFormat, thumbQuality); } - thumb = App::pixmapFromImageInPlace(std::move(cover)); - thumbSize = MTP_photoSize(MTP_string(""), MTP_fileLocationUnavailable(MTP_long(0), MTP_int(0), MTP_long(0)), MTP_int(thumb.width()), MTP_int(thumb.height()), MTP_int(0)); - thumbId = rand_value(); + thumb = std::move(cover); + thumbSize = MTP_photoSize(MTP_string(""), MTP_fileLocationUnavailable(MTP_long(0), MTP_int(0), MTP_long(0)), MTP_int(thumb.width()), MTP_int(thumb.height()), MTP_int(0)); } } @@ -603,15 +602,15 @@ void FileLoadTask::process() { if (isAnimation) { attributes.push_back(MTP_documentAttributeAnimated()); } else if (_type != SendMediaType::File) { - auto thumb = (w > 100 || h > 100) ? App::pixmapFromImageInPlace(fullimage.scaled(100, 100, Qt::KeepAspectRatio, Qt::SmoothTransformation)) : QPixmap::fromImage(fullimage); + auto thumb = (w > 100 || h > 100) ? fullimage.scaled(100, 100, Qt::KeepAspectRatio, Qt::SmoothTransformation) : fullimage; photoThumbs.insert('s', thumb); photoSizes.push_back(MTP_photoSize(MTP_string("s"), MTP_fileLocationUnavailable(MTP_long(0), MTP_int(0), MTP_long(0)), MTP_int(thumb.width()), MTP_int(thumb.height()), MTP_int(0))); - auto medium = (w > 320 || h > 320) ? App::pixmapFromImageInPlace(fullimage.scaled(320, 320, Qt::KeepAspectRatio, Qt::SmoothTransformation)) : QPixmap::fromImage(fullimage); + auto medium = (w > 320 || h > 320) ? fullimage.scaled(320, 320, Qt::KeepAspectRatio, Qt::SmoothTransformation) : fullimage; photoThumbs.insert('m', medium); photoSizes.push_back(MTP_photoSize(MTP_string("m"), MTP_fileLocationUnavailable(MTP_long(0), MTP_int(0), MTP_long(0)), MTP_int(medium.width()), MTP_int(medium.height()), MTP_int(0))); - auto full = (w > 1280 || h > 1280) ? App::pixmapFromImageInPlace(fullimage.scaled(1280, 1280, Qt::KeepAspectRatio, Qt::SmoothTransformation)) : QPixmap::fromImage(fullimage); + auto full = (w > 1280 || h > 1280) ? fullimage.scaled(1280, 1280, Qt::KeepAspectRatio, Qt::SmoothTransformation) : fullimage; photoThumbs.insert('y', full); photoSizes.push_back(MTP_photoSize(MTP_string("y"), MTP_fileLocationUnavailable(MTP_long(0), MTP_int(0), MTP_long(0)), MTP_int(full.width()), MTP_int(full.height()), MTP_int(0))); @@ -647,17 +646,13 @@ void FileLoadTask::process() { thumbname = qsl("thumb.webp"); } - QPixmap full = (w > 90 || h > 90) ? App::pixmapFromImageInPlace(fullimage.scaled(90, 90, Qt::KeepAspectRatio, Qt::SmoothTransformation)) : QPixmap::fromImage(fullimage, Qt::ColorOnly); - + thumbId = rand_value(); + thumb = (w > 90 || h > 90) ? fullimage.scaled(90, 90, Qt::KeepAspectRatio, Qt::SmoothTransformation) : fullimage; + thumbSize = MTP_photoSize(MTP_string(""), MTP_fileLocationUnavailable(MTP_long(0), MTP_int(0), MTP_long(0)), MTP_int(thumb.width()), MTP_int(thumb.height()), MTP_int(0)); { QBuffer buffer(&thumbdata); - full.save(&buffer, thumbFormat, thumbQuality); + thumb.save(&buffer, thumbFormat, thumbQuality); } - - thumb = full; - thumbSize = MTP_photoSize(MTP_string(""), MTP_fileLocationUnavailable(MTP_long(0), MTP_int(0), MTP_long(0)), MTP_int(full.width()), MTP_int(full.height()), MTP_int(0)); - - thumbId = rand_value(); } } @@ -704,7 +699,7 @@ void FileLoadTask::process() { _result->thumbId = thumbId; _result->thumbname = thumbname; _result->setThumbData(thumbdata); - _result->thumb = thumb; + _result->thumb = std::move(thumb); _result->photo = photo; _result->document = document; diff --git a/Telegram/SourceFiles/storage/localimageloader.h b/Telegram/SourceFiles/storage/localimageloader.h index cc986ca30..239d7086d 100644 --- a/Telegram/SourceFiles/storage/localimageloader.h +++ b/Telegram/SourceFiles/storage/localimageloader.h @@ -214,7 +214,7 @@ struct FileLoadResult { QString thumbname; UploadFileParts thumbparts; QByteArray thumbmd5; - QPixmap thumb; + QImage thumb; MTPPhoto photo; MTPDocument document; diff --git a/Telegram/SourceFiles/ui/image.cpp b/Telegram/SourceFiles/ui/image.cpp index bbcbdcf3b..d010d9fe0 100644 --- a/Telegram/SourceFiles/ui/image.cpp +++ b/Telegram/SourceFiles/ui/image.cpp @@ -16,20 +16,11 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL namespace { -using LocalImages = QMap; -LocalImages localImages; - -using WebImages = QMap; -WebImages webImages; - -using StorageImages = QMap; -StorageImages storageImages; - -using WebFileImages = QMap; -WebFileImages webFileImages; - -using GeoPointImages = QMap; -GeoPointImages geoPointImages; +QMap LocalFileImages; +QMap WebUrlImages; +QMap StorageImages; +QMap WebCachedImages; +QMap GeoPointImages; int64 GlobalAcquiredSize = 0; int64 LocalAcquiredSize = 0; @@ -47,24 +38,23 @@ uint64 SinglePixKey(Images::Options options) { namespace Images { void ClearRemote() { - for (auto image : base::take(storageImages)) { + for (auto image : base::take(StorageImages)) { delete image; } - for (auto image : base::take(webImages)) { + for (auto image : base::take(WebUrlImages)) { delete image; } - for (auto image : base::take(webFileImages)) { + for (auto image : base::take(WebCachedImages)) { delete image; } - for (auto image : base::take(geoPointImages)) { + for (auto image : base::take(GeoPointImages)) { delete image; } - LocalAcquiredSize = GlobalAcquiredSize; } void ClearAll() { - for (auto image : base::take(localImages)) { + for (auto image : base::take(LocalFileImages)) { delete image; } ClearRemote(); @@ -78,38 +68,714 @@ void CheckCacheSize() { } } +Source::~Source() = default; + +ImageSource::ImageSource(QImage &&data, const QByteArray &format) +: _data(std::move(data)) +, _format(format) { +} + +void ImageSource::load( + Data::FileOrigin origin, + bool loadFirst, + bool prior) { +} + +void ImageSource::loadEvenCancelled( + Data::FileOrigin origin, + bool loadFirst, + bool prior) { +} + +QImage ImageSource::takeLoaded() { + return _data; +} + +void ImageSource::forget() { +} + +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 _data.width(); +} + +int ImageSource::height() { + return _data.height(); +} + +void ImageSource::setInformation(int size, int width, int 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)) { +} + +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::forget() { + _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; +} + +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()) { + destroyLoaderDelayed(CancelledFileLoader); + return QImage(); + } + + setInformation(_loader->bytes().size(), data.width(), data.height()); + + destroyLoaderDelayed(); + + return data; +} + +bool RemoteSource::loaderValid() const { + return _loader && _loader != CancelledFileLoader; +} + +void RemoteSource::destroyLoaderDelayed(FileLoader *newValue) { + Expects(loaderValid()); + + _loader->stop(); + auto loader = std::unique_ptr(std::exchange(_loader, newValue)); + Auth().downloader().delayedDestroyLoader(std::move(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; + } + _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); +} + +RemoteSource::~RemoteSource() { + forget(); +} + +bool RemoteSource::displayLoading() { + return loaderValid() + && (!_loader->loadingLocal() || !_loader->autoLoading()); +} + +void RemoteSource::cancel() { + if (!loaderValid()) return; + + const auto loader = std::exchange(_loader, CancelledFileLoader); + loader->cancel(); + loader->stop(); + Auth().downloader().delayedDestroyLoader( + std::unique_ptr(loader)); +} + +void RemoteSource::forget() { + if (loaderValid()) { + destroyLoaderDelayed(); + } +} + +float64 RemoteSource::progress() { + return loaderValid() ? _loader->currentProgress() : 0.; +} + +int RemoteSource::loadOffset() { + return loaderValid() ? _loader->currentOffset() : 0; +} + +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(); +} + +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; +} + +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; +} + +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; +} + +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 -Image::Image(const QString &file, QByteArray fmt) { - _data = App::pixmapFromImageInPlace(App::readImage(file, &fmt, false, 0, &_saved)); - _format = fmt; - if (!_data.isNull()) { - GlobalAcquiredSize += int64(_data.width()) * _data.height() * 4; - } -} - -Image::Image(const QByteArray &filecontent, QByteArray fmt) { - _data = App::pixmapFromImageInPlace(App::readImage(filecontent, &fmt, false)); - _format = fmt; - _saved = filecontent; - if (!_data.isNull()) { - GlobalAcquiredSize += int64(_data.width()) * _data.height() * 4; - } -} - -Image::Image(const QPixmap &pixmap, QByteArray format) : _format(format), _data(pixmap) { - if (!_data.isNull()) { - GlobalAcquiredSize += int64(_data.width()) * _data.height() * 4; - } -} - -Image::Image(const QByteArray &filecontent, QByteArray fmt, const QPixmap &pixmap) : _saved(filecontent), _format(fmt), _data(pixmap) { - _data = pixmap; - _format = fmt; - _saved = filecontent; - if (!_data.isNull()) { - GlobalAcquiredSize += int64(_data.width()) * _data.height() * 4; - } +Image::Image(std::unique_ptr &&source) +: _source(std::move(source)) { } Image *Image::Blank() { @@ -122,7 +788,7 @@ Image *Image::Blank() { data.fill(Qt::transparent); data.setDevicePixelRatio(cRetinaFactor()); return Images::details::Create( - App::pixmapFromImageInPlace(std::move(data)), + std::move(data), "GIF"); }(); return blankImage; @@ -136,7 +802,7 @@ const QPixmap &Image::pix( Data::FileOrigin origin, int32 w, int32 h) const { - checkload(); + checkSource(); if (w <= 0 || !width() || !height()) { w = width(); @@ -164,7 +830,7 @@ const QPixmap &Image::pixRounded( int32 h, ImageRoundRadius radius, RectParts corners) const { - checkload(); + checkSource(); if (w <= 0 || !width() || !height()) { w = width(); @@ -203,7 +869,7 @@ const QPixmap &Image::pixCircled( Data::FileOrigin origin, int32 w, int32 h) const { - checkload(); + checkSource(); if (w <= 0 || !width() || !height()) { w = width(); @@ -229,7 +895,7 @@ const QPixmap &Image::pixBlurredCircled( Data::FileOrigin origin, int32 w, int32 h) const { - checkload(); + checkSource(); if (w <= 0 || !width() || !height()) { w = width(); @@ -255,7 +921,7 @@ const QPixmap &Image::pixBlurred( Data::FileOrigin origin, int32 w, int32 h) const { - checkload(); + checkSource(); if (w <= 0 || !width() || !height()) { w = width() * cIntRetinaFactor(); @@ -282,7 +948,7 @@ const QPixmap &Image::pixColored( style::color add, int32 w, int32 h) const { - checkload(); + checkSource(); if (w <= 0 || !width() || !height()) { w = width() * cIntRetinaFactor(); @@ -309,7 +975,7 @@ const QPixmap &Image::pixBlurredColored( style::color add, int32 w, int32 h) const { - checkload(); + checkSource(); if (w <= 0 || !width() || !height()) { w = width() * cIntRetinaFactor(); @@ -340,7 +1006,7 @@ const QPixmap &Image::pixSingle( ImageRoundRadius radius, RectParts corners, const style::color *colored) const { - checkload(); + checkSource(); if (w <= 0 || !width() || !height()) { w = width() * cIntRetinaFactor(); @@ -385,13 +1051,13 @@ const QPixmap &Image::pixSingle( const QPixmap &Image::pixBlurredSingle( Data::FileOrigin origin, - int w, - int h, + int32 w, + int32 h, int32 outerw, int32 outerh, ImageRoundRadius radius, RectParts corners) const { - checkload(); + checkSource(); if (w <= 0 || !width() || !height()) { w = width() * cIntRetinaFactor(); @@ -439,8 +1105,10 @@ QPixmap Image::pixNoCache( int outerw, int outerh, const style::color *colored) const { - if (!loading()) const_cast(this)->load(origin); - restore(); + if (!loading()) { + const_cast(this)->load(origin); + } + checkSource(); if (_data.isNull()) { if (h <= 0 && height() > 0) { @@ -489,7 +1157,7 @@ QPixmap Image::pixNoCache( return App::pixmapFromImageInPlace(std::move(result)); } - return Images::pixmap(_data.toImage(), w, h, options, outerw, outerh, colored); + return Images::pixmap(_data, w, h, options, outerw, outerh, colored); } QPixmap Image::pixColoredNoCache( @@ -498,13 +1166,16 @@ QPixmap Image::pixColoredNoCache( int32 w, int32 h, bool smooth) const { - const_cast(this)->load(origin); - restore(); + if (!loading()) { + const_cast(this)->load(origin); + } + checkSource(); + if (_data.isNull()) { return Blank()->pix(origin); } - auto img = _data.toImage(); + auto img = _data; if (w <= 0 || !width() || !height() || (w == width() && (h <= 0 || h == height()))) { return App::pixmapFromImageInPlace(Images::prepareColored(add, std::move(img))); } @@ -519,13 +1190,16 @@ QPixmap Image::pixBlurredColoredNoCache( style::color add, int32 w, int32 h) const { - const_cast(this)->load(origin); - restore(); + if (!loading()) { + const_cast(this)->load(origin); + } + checkSource(); + if (_data.isNull()) { return Blank()->pix(origin); } - auto img = Images::prepareBlur(_data.toImage()); + auto img = Images::prepareBlur(_data); if (h <= 0) { img = img.scaledToWidth(w, Qt::SmoothTransformation); } else { @@ -535,512 +1209,61 @@ QPixmap Image::pixBlurredColoredNoCache( return App::pixmapFromImageInPlace(Images::prepareColored(add, img)); } -void Image::forget() const { - if (_forgot) return; +std::optional Image::cacheKey() const { + return _source->cacheKey(); +} - checkload(); - if (_data.isNull()) return; +bool Image::loaded() const { + checkSource(); + return !_data.isNull(); +} - invalidateSizeCache(); - /*if (hasLocalCopy()) { - _saved.clear(); - } else */if (_saved.isEmpty()) { - QBuffer buffer(&_saved); - if (!_data.save(&buffer, _format)) { - if (_data.save(&buffer, "PNG")) { - _format = "PNG"; - } else { - return; - } +void Image::checkSource() const { + auto data = _source->takeLoaded(); + if (_data.isNull() && !data.isNull()) { + invalidateSizeCache(); + _data = std::move(data); + if (!_data.isNull()) { + GlobalAcquiredSize += int64(_data.width()) * _data.height() * 4; } } - GlobalAcquiredSize -= int64(_data.width()) * _data.height() * 4; - _data = QPixmap(); - _forgot = true; } -void Image::restore() const { - if (!_forgot) return; - - QBuffer buffer(&_saved); - QImageReader reader(&buffer, _format); -#ifndef OS_MAC_OLD - reader.setAutoTransform(true); -#endif // OS_MAC_OLD - _data = QPixmap::fromImageReader(&reader, Qt::ColorOnly); - +void Image::forget() const { + _source->takeLoaded(); + _source->forget(); + invalidateSizeCache(); if (!_data.isNull()) { - GlobalAcquiredSize += int64(_data.width()) * _data.height() * 4; + GlobalAcquiredSize -= int64(_data.width()) * _data.height() * 4; + _data = QImage(); } - _forgot = false; } -std::optional Image::cacheKey() const { - return std::nullopt; +void Image::setDelayedStorageLocation( + Data::FileOrigin origin, + const StorageImageLocation &location) { + _source->setDelayedStorageLocation(location); + if (!loaded()) { + _source->performDelayedLoad(origin); + } +} + +void Image::setImageBytes(const QByteArray &bytes) { + _source->setImageBytes(bytes); + checkSource(); } void Image::invalidateSizeCache() const { - for (auto &pix : _sizesCache) { - if (!pix.isNull()) { - GlobalAcquiredSize -= int64(pix.width()) * pix.height() * 4; + for (const auto &image : std::as_const(_sizesCache)) { + if (!image.isNull()) { + GlobalAcquiredSize -= int64(image.width()) * image.height() * 4; } } _sizesCache.clear(); } Image::~Image() { - invalidateSizeCache(); - if (!_data.isNull()) { - GlobalAcquiredSize -= int64(_data.width()) * _data.height() * 4; - } -} - -void RemoteImage::doCheckload() const { - if (!amLoading() || !_loader->finished()) return; - - QPixmap data = _loader->imagePixmap(shrinkBox()); - if (data.isNull()) { - destroyLoaderDelayed(CancelledFileLoader); - return; - } - - if (!_data.isNull()) { - GlobalAcquiredSize -= int64(_data.width()) * _data.height() * 4; - } - - _format = _loader->imageFormat(shrinkBox()); - _data = data; - _saved = _loader->bytes(); - const_cast(this)->setInformation(_saved.size(), _data.width(), _data.height()); - GlobalAcquiredSize += int64(_data.width()) * _data.height() * 4; - - invalidateSizeCache(); - - destroyLoaderDelayed(); - - _forgot = false; -} - -void RemoteImage::destroyLoaderDelayed(FileLoader *newValue) const { - _loader->stop(); - auto loader = std::unique_ptr(std::exchange(_loader, newValue)); - Auth().downloader().delayedDestroyLoader(std::move(loader)); -} - -void RemoteImage::loadLocal() { - if (loaded() || amLoading()) return; - - _loader = createLoader(std::nullopt, LoadFromLocalOnly, true); - if (_loader) _loader->start(); -} - -void RemoteImage::setImageBytes( - const QByteArray &bytes, - const QByteArray &bytesFormat) { - if (!_data.isNull()) { - GlobalAcquiredSize -= int64(_data.width()) * _data.height() * 4; - } - QByteArray fmt(bytesFormat); - _data = App::pixmapFromImageInPlace(App::readImage(bytes, &fmt, false)); - if (!_data.isNull()) { - GlobalAcquiredSize += int64(_data.width()) * _data.height() * 4; - setInformation(bytes.size(), _data.width(), _data.height()); - } - - invalidateSizeCache(); - if (amLoading()) { - destroyLoaderDelayed(); - } - _saved = bytes; - _format = fmt; - _forgot = false; - - 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 RemoteImage::amLoading() const { - return _loader && _loader != CancelledFileLoader; -} - -void RemoteImage::automaticLoad( - Data::FileOrigin origin, - const HistoryItem *item) { - if (loaded()) return; - - 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 RemoteImage::automaticLoadSettingsChanged() { - if (loaded() || _loader != CancelledFileLoader) return; - _loader = 0; -} - -void RemoteImage::load( - Data::FileOrigin origin, - bool loadFirst, - bool prior) { - if (loaded()) return; - - if (!_loader) { - _loader = createLoader(origin, LoadFromCloudOrLocal, false); - } - if (amLoading()) { - _loader->start(loadFirst, prior); - } -} - -void RemoteImage::loadEvenCancelled( - Data::FileOrigin origin, - bool loadFirst, - bool prior) { - if (_loader == CancelledFileLoader) { - _loader = nullptr; - } - return load(origin, loadFirst, prior); -} - -RemoteImage::~RemoteImage() { - if (!_data.isNull()) { - GlobalAcquiredSize -= int64(_data.width()) * _data.height() * 4; - } - if (amLoading()) { - destroyLoaderDelayed(); - } -} - -bool RemoteImage::loaded() const { - doCheckload(); - return (!_data.isNull() || !_saved.isNull()); -} - -bool RemoteImage::displayLoading() const { - return amLoading() && (!_loader->loadingLocal() || !_loader->autoLoading()); -} - -void RemoteImage::cancel() { - if (!amLoading()) return; - - auto loader = std::exchange(_loader, CancelledFileLoader); - loader->cancel(); - loader->stop(); - Auth().downloader().delayedDestroyLoader(std::unique_ptr(loader)); -} - -float64 RemoteImage::progress() const { - return amLoading() ? _loader->currentProgress() : (loaded() ? 1 : 0); -} - -int32 RemoteImage::loadOffset() const { - return amLoading() ? _loader->currentOffset() : 0; -} - -StorageImage::StorageImage(const StorageImageLocation &location, int32 size) -: _location(location) -, _size(size) { -} - -StorageImage::StorageImage( - const StorageImageLocation &location, - const QByteArray &bytes) -: _location(location) -, _size(bytes.size()) { - setImageBytes(bytes); -} - -std::optional StorageImage::cacheKey() const { - return _location.isNull() - ? std::nullopt - : base::make_optional(Data::StorageCacheKey(_location)); -} - -int32 StorageImage::countWidth() const { - return _location.width(); -} - -int32 StorageImage::countHeight() const { - return _location.height(); -} - -void StorageImage::setInformation(int32 size, int32 width, int32 height) { - _size = size; - _location.setSize(width, height); -} - -FileLoader *StorageImage::createLoader( - Data::FileOrigin origin, - LoadFromCloudSetting fromCloud, - bool autoLoading) { - if (_location.isNull()) { - return nullptr; - } - return new mtpFileLoader( - &_location, - origin, - _size, - fromCloud, - autoLoading, - Data::kImageCacheTag); -} - -WebFileImage::WebFileImage( - const WebFileLocation &location, - QSize box, - int size) -: _location(location) -, _box(box) -, _width(0) -, _height(0) -, _size(size) { -} - -WebFileImage::WebFileImage( - const WebFileLocation &location, - int width, - int height, - int size) -: _location(location) -, _width(width) -, _height(height) -, _size(size) { -} - -std::optional WebFileImage::cacheKey() const { - return _location.isNull() - ? std::nullopt - : base::make_optional(Data::WebDocumentCacheKey(_location)); -} - -int WebFileImage::countWidth() const { - return _width; -} - -int WebFileImage::countHeight() const { - return _height; -} - -void WebFileImage::setInformation(int size, int width, int height) { - _size = size; - _width = width; - _height = height; -} - -FileLoader *WebFileImage::createLoader( - Data::FileOrigin origin, - LoadFromCloudSetting fromCloud, - bool autoLoading) { - return _location.isNull() - ? nullptr - : new mtpFileLoader( - &_location, - _size, - fromCloud, - autoLoading, - Data::kImageCacheTag); -} - -GeoPointImage::GeoPointImage(const GeoPointLocation &location) -: _location(location) { -} - -std::optional GeoPointImage::cacheKey() const { - return Data::GeoPointCacheKey(_location); -} - -int GeoPointImage::countWidth() const { - return _location.width * _location.scale; -} - -int GeoPointImage::countHeight() const { - return _location.height * _location.scale; -} - -void GeoPointImage::setInformation(int size, int width, int height) { - _size = size; - _location.width = width; - _location.height = height; -} - -FileLoader *GeoPointImage::createLoader( - Data::FileOrigin origin, - LoadFromCloudSetting fromCloud, - bool autoLoading) { - return new mtpFileLoader( - &_location, - _size, - fromCloud, - autoLoading, - Data::kImageCacheTag); -} - -DelayedStorageImage::DelayedStorageImage() -: StorageImage(StorageImageLocation()) -, _loadRequested(false) -, _loadCancelled(false) -, _loadFromCloud(false) { -} - -DelayedStorageImage::DelayedStorageImage(int32 w, int32 h) -: StorageImage(StorageImageLocation(w, h, 0, 0, 0, 0, {})) -, _loadRequested(false) -, _loadCancelled(false) -, _loadFromCloud(false) { -} -// -//DelayedStorageImage::DelayedStorageImage(QByteArray &bytes) -//: StorageImage(StorageImageLocation(), bytes) -//, _loadRequested(false) -//, _loadCancelled(false) -//, _loadFromCloud(false) { -//} - -void DelayedStorageImage::setDelayedStorageLocation( - Data::FileOrigin origin, - const StorageImageLocation location) { - _location = location; - if (_loadRequested) { - if (!_loadCancelled) { - if (_loadFromCloud) { - load(origin); - } else { - loadLocal(); - } - } - _loadRequested = false; - } -} - -void DelayedStorageImage::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 { - StorageImage::automaticLoad(origin, item); - } -} - -void DelayedStorageImage::automaticLoadSettingsChanged() { - if (_loadCancelled) _loadCancelled = false; - StorageImage::automaticLoadSettingsChanged(); -} - -void DelayedStorageImage::load( - Data::FileOrigin origin, - bool loadFirst, - bool prior) { - if (_location.isNull()) { - _loadRequested = _loadFromCloud = true; - } else { - StorageImage::load(origin, loadFirst, prior); - } -} - -void DelayedStorageImage::loadEvenCancelled( - Data::FileOrigin origin, - bool loadFirst, - bool prior) { - _loadCancelled = false; - StorageImage::loadEvenCancelled(origin, loadFirst, prior); -} - -bool DelayedStorageImage::displayLoading() const { - return _location.isNull() ? true : StorageImage::displayLoading(); -} - -void DelayedStorageImage::cancel() { - if (_loadRequested) { - _loadRequested = false; - } - StorageImage::cancel(); -} - -WebImage::WebImage(const QString &url, QSize box) -: _url(url) -, _box(box) -, _size(0) -, _width(0) -, _height(0) { -} - -WebImage::WebImage(const QString &url, int width, int height) -: _url(url) -, _size(0) -, _width(width) -, _height(height) { -} - -std::optional WebImage::cacheKey() const { - return Data::UrlCacheKey(_url); -} - -void WebImage::setSize(int width, int height) { - _width = width; - _height = height; -} - -int32 WebImage::countWidth() const { - return _width; -} - -int32 WebImage::countHeight() const { - return _height; -} - -void WebImage::setInformation(int32 size, int32 width, int32 height) { - _size = size; - setSize(width, height); -} - -FileLoader *WebImage::createLoader( - Data::FileOrigin origin, - LoadFromCloudSetting fromCloud, - bool autoLoading) { - return new webFileLoader( - _url, - QString(), - fromCloud, - autoLoading, - Data::kImageCacheTag); + forget(); } namespace Images { @@ -1049,83 +1272,111 @@ namespace details { Image *Create(const QString &file, QByteArray format) { if (file.startsWith(qstr("http://"), Qt::CaseInsensitive) || file.startsWith(qstr("https://"), Qt::CaseInsensitive)) { - QString key = file; - WebImages::const_iterator i = webImages.constFind(key); - if (i == webImages.cend()) { - i = webImages.insert(key, new WebImage(file)); + const auto key = file; + auto i = WebUrlImages.constFind(key); + if (i == WebUrlImages.cend()) { + i = WebUrlImages.insert( + key, + new Image(std::make_unique(file))); } return i.value(); } else { QFileInfo f(file); - QString key = qsl("//:%1//:%2//:").arg(f.size()).arg(f.lastModified().toTime_t()) + file; - LocalImages::const_iterator i = localImages.constFind(key); - if (i == localImages.cend()) { - i = localImages.insert(key, new Image(file, format)); + const auto key = qsl("//:%1//:%2//:" + ).arg(f.size() + ).arg(f.lastModified().toTime_t() + ) + file; + auto i = LocalFileImages.constFind(key); + if (i == LocalFileImages.cend()) { + i = LocalFileImages.insert( + key, + new Image(std::make_unique( + file, + QByteArray(), + format))); } return i.value(); } } Image *Create(const QString &url, QSize box) { - QString key = qsl("//:%1//:%2//:").arg(box.width()).arg(box.height()) + url; - auto i = webImages.constFind(key); - if (i == webImages.cend()) { - i = webImages.insert(key, new WebImage(url, box)); + const auto key = qsl("//:%1//:%2//:").arg(box.width()).arg(box.height()) + url; + auto i = WebUrlImages.constFind(key); + if (i == WebUrlImages.cend()) { + i = WebUrlImages.insert( + key, + new Image(std::make_unique(url, box))); } return i.value(); } Image *Create(const QString &url, int width, int height) { - QString key = url; - auto i = webImages.constFind(key); - if (i == webImages.cend()) { - i = webImages.insert(key, new WebImage(url, width, height)); + const auto key = url; + auto i = WebUrlImages.constFind(key); + if (i == WebUrlImages.cend()) { + i = WebUrlImages.insert( + key, + new Image(std::make_unique(url, width, height))); } else { - i.value()->setSize(width, height); + i.value()->setInformation(0, width, height); } return i.value(); } Image *Create(const QByteArray &filecontent, QByteArray format) { - return new Image(filecontent, format); + auto image = App::readImage(filecontent, &format, false); + return Create(filecontent, format, std::move(image)); } -Image *Create(const QPixmap &pixmap, QByteArray format) { - return new Image(pixmap, format); +Image *Create(QImage &&image, QByteArray format) { + return new Image(std::make_unique( + std::move(image), + format)); } -Image *Create(const QByteArray &filecontent, QByteArray format, const QPixmap &pixmap) { - return new Image(filecontent, format, pixmap); +Image *Create( + const QByteArray &filecontent, + QByteArray format, + QImage &&image) { + return new Image(std::make_unique( + QString(), + filecontent, + format, + std::move(image))); } -Image *Create(int32 width, int32 height) { - return new DelayedStorageImage(width, height); +Image *Create(int width, int height) { + return new Image(std::make_unique(width, height)); } -StorageImage *Create(const StorageImageLocation &location, int32 size) { +Image *Create(const StorageImageLocation &location, int size) { const auto key = storageKey(location); - auto i = storageImages.constFind(key); - if (i == storageImages.cend()) { - i = storageImages.insert(key, new StorageImage(location, size)); + auto i = StorageImages.constFind(key); + if (i == StorageImages.cend()) { + i = StorageImages.insert( + key, + new Image(std::make_unique(location, size))); } else { i.value()->refreshFileReference(location.fileReference()); } return i.value(); } -StorageImage *Create( +Image *Create( const StorageImageLocation &location, const QByteArray &bytes) { const auto key = storageKey(location); - auto i = storageImages.constFind(key); - if (i == storageImages.cend()) { - i = storageImages.insert(key, new StorageImage(location, bytes)); + auto i = StorageImages.constFind(key); + if (i == StorageImages.cend()) { + i = StorageImages.insert( + key, + new Image(std::make_unique( + location, + bytes.size()))); } else { i.value()->refreshFileReference(location.fileReference()); - if (!i.value()->loaded()) { - i.value()->setImageBytes(bytes); - } } + i.value()->setImageBytes(bytes); return i.value(); } @@ -1214,42 +1465,49 @@ Image *Create(const MTPWebDocument &document, QSize box) { Unexpected("Type in getImage(MTPWebDocument)."); } -WebFileImage *Create( +Image *Create( const WebFileLocation &location, QSize box, int size) { - auto key = storageKey(location); - auto i = webFileImages.constFind(key); - if (i == webFileImages.cend()) { - i = webFileImages.insert( + const auto key = storageKey(location); + auto i = WebCachedImages.constFind(key); + if (i == WebCachedImages.cend()) { + i = WebCachedImages.insert( key, - new WebFileImage(location, box, size)); + new Image(std::make_unique( + location, + box, + size))); } return i.value(); } -WebFileImage *Create( +Image *Create( const WebFileLocation &location, int width, int height, int size) { - auto key = storageKey(location); - auto i = webFileImages.constFind(key); - if (i == webFileImages.cend()) { - i = webFileImages.insert( + const auto key = storageKey(location); + auto i = WebCachedImages.constFind(key); + if (i == WebCachedImages.cend()) { + i = WebCachedImages.insert( key, - new WebFileImage(location, width, height, size)); + new Image(std::make_unique( + location, + width, + height, + size))); } return i.value(); } -GeoPointImage *Create(const GeoPointLocation &location) { - auto key = storageKey(location); - auto i = geoPointImages.constFind(key); - if (i == geoPointImages.cend()) { - i = geoPointImages.insert( +Image *Create(const GeoPointLocation &location) { + const auto key = storageKey(location); + auto i = GeoPointImages.constFind(key); + if (i == GeoPointImages.cend()) { + i = GeoPointImages.insert( key, - new GeoPointImage(location)); + new Image(std::make_unique(location))); } return i.value(); } diff --git a/Telegram/SourceFiles/ui/image.h b/Telegram/SourceFiles/ui/image.h index 2932058ca..5e33a5f94 100644 --- a/Telegram/SourceFiles/ui/image.h +++ b/Telegram/SourceFiles/ui/image.h @@ -13,44 +13,363 @@ void ClearRemote(); void ClearAll(); void CheckCacheSize(); +class Source { +public: + virtual ~Source(); + + virtual void load( + Data::FileOrigin origin, + bool loadFirst, + bool prior) = 0; + virtual void loadEvenCancelled( + Data::FileOrigin origin, + bool loadFirst, + bool prior) = 0; + virtual QImage takeLoaded() = 0; + virtual void forget() = 0; + + virtual void automaticLoad( + Data::FileOrigin origin, + const HistoryItem *item) = 0; + virtual void automaticLoadSettingsChanged() = 0; + + virtual bool loading() = 0; + virtual bool displayLoading() = 0; + virtual void cancel() = 0; + virtual float64 progress() = 0; + virtual int loadOffset() = 0; + + virtual const StorageImageLocation &location() = 0; + virtual void refreshFileReference(const QByteArray &data) = 0; + virtual std::optional cacheKey() = 0; + virtual void setDelayedStorageLocation( + const StorageImageLocation &location) = 0; + virtual void performDelayedLoad(Data::FileOrigin origin) = 0; + virtual bool isDelayedStorageImage() const = 0; + virtual void setImageBytes(const QByteArray &bytes) = 0; + + virtual int width() = 0; + virtual int height() = 0; + virtual void setInformation(int size, int width, int height) = 0; + + virtual QByteArray bytesForCache() = 0; + +}; + +class ImageSource : public Source { +public: + ImageSource(QImage &&data, const QByteArray &format); + + void load( + Data::FileOrigin origin, + bool loadFirst, + bool prior) override; + void loadEvenCancelled( + Data::FileOrigin origin, + bool loadFirst, + bool prior) override; + QImage takeLoaded() override; + void forget() override; + + void automaticLoad( + Data::FileOrigin origin, + const HistoryItem *item) override; + void automaticLoadSettingsChanged() override; + + bool loading() override; + bool displayLoading() override; + void cancel() override; + float64 progress() override; + int loadOffset() override; + + const StorageImageLocation &location() override; + void refreshFileReference(const QByteArray &data) override; + std::optional cacheKey() override; + void setDelayedStorageLocation( + const StorageImageLocation &location) override; + void performDelayedLoad(Data::FileOrigin origin) override; + bool isDelayedStorageImage() const override; + void setImageBytes(const QByteArray &bytes) override; + + int width() override; + int height() override; + void setInformation(int size, int width, int height) override; + + QByteArray bytesForCache() override; + +private: + QImage _data; + QByteArray _format; + +}; + +class LocalFileSource : public Source { +public: + LocalFileSource( + const QString &path, + const QByteArray &content, + const QByteArray &format, + QImage &&data = QImage()); + + void load( + Data::FileOrigin origin, + bool loadFirst, + bool prior) override; + void loadEvenCancelled( + Data::FileOrigin origin, + bool loadFirst, + bool prior) override; + QImage takeLoaded() override; + void forget() override; + + void automaticLoad( + Data::FileOrigin origin, + const HistoryItem *item) override; + void automaticLoadSettingsChanged() override; + + bool loading() override; + bool displayLoading() override; + void cancel() override; + float64 progress() override; + int loadOffset() override; + + const StorageImageLocation &location() override; + void refreshFileReference(const QByteArray &data) override; + std::optional cacheKey() override; + void setDelayedStorageLocation( + const StorageImageLocation &location) override; + void performDelayedLoad(Data::FileOrigin origin) override; + bool isDelayedStorageImage() const override; + void setImageBytes(const QByteArray &bytes) override; + + int width() override; + int height() override; + void setInformation(int size, int width, int height) override; + + QByteArray bytesForCache() override; + +private: + void ensureDimensionsKnown(); + + QString _path; + QByteArray _bytes; + QByteArray _format; + QImage _data; + int _width = 0; + int _height = 0; + +}; + +class RemoteSource : public Source { +public: + void load( + Data::FileOrigin origin, + bool loadFirst, + bool prior) override; + void loadEvenCancelled( + Data::FileOrigin origin, + bool loadFirst, + bool prior) override; + QImage takeLoaded() override; + void forget() override; + + void automaticLoad( + Data::FileOrigin origin, + const HistoryItem *item) override; + void automaticLoadSettingsChanged() override; + + bool loading() override; + bool displayLoading() override; + void cancel() override; + float64 progress() override; + int loadOffset() override; + + const StorageImageLocation &location() override; + void refreshFileReference(const QByteArray &data) override; + void setDelayedStorageLocation( + const StorageImageLocation &location) override; + void performDelayedLoad(Data::FileOrigin origin) override; + bool isDelayedStorageImage() const override; + void setImageBytes(const QByteArray &bytes) override; + + QByteArray bytesForCache() override; + + ~RemoteSource(); + +protected: + // If after loading the image we need to shrink it to fit into a + // specific size, you can return this size here. + virtual QSize shrinkBox() const = 0; + virtual FileLoader *createLoader( + Data::FileOrigin origin, + LoadFromCloudSetting fromCloud, + bool autoLoading) = 0; + + void loadLocal(); + +private: + bool loaderValid() const; + void destroyLoaderDelayed(FileLoader *newValue = nullptr); + + FileLoader *_loader = nullptr; + +}; + +class StorageSource : public RemoteSource { +public: + StorageSource( + const StorageImageLocation &location, + int size); + + const StorageImageLocation &location() override; + std::optional cacheKey() override; + + void refreshFileReference(const QByteArray &data) override; + + int width() override; + int height() override; + void setInformation(int size, int width, int height) override; + +protected: + QSize shrinkBox() const override; + FileLoader *createLoader( + Data::FileOrigin origin, + LoadFromCloudSetting fromCloud, + bool autoLoading) override; + + StorageImageLocation _location; + int _size = 0; + +}; + +class WebCachedSource : public RemoteSource { +public: + WebCachedSource(const WebFileLocation &location, QSize box, int size = 0); + WebCachedSource( + const WebFileLocation &location, + int width, + int height, + int size = 0); + + std::optional cacheKey() override; + + int width() override; + int height() override; + void setInformation(int size, int width, int height) override; + +protected: + QSize shrinkBox() const override; + FileLoader *createLoader( + Data::FileOrigin origin, + LoadFromCloudSetting fromCloud, + bool autoLoading) override; + + WebFileLocation _location; + QSize _box; + int _width = 0; + int _height = 0; + int _size = 0; + +}; + +class GeoPointSource : public RemoteSource { +public: + GeoPointSource(const GeoPointLocation &location); + + std::optional cacheKey() override; + + int width() override; + int height() override; + void setInformation(int size, int width, int height) override; + +protected: + QSize shrinkBox() const override; + FileLoader *createLoader( + Data::FileOrigin origin, + LoadFromCloudSetting fromCloud, + bool autoLoading) override; + + GeoPointLocation _location; + int _size = 0; + +}; + +class DelayedStorageSource : public StorageSource { +public: + DelayedStorageSource(); + DelayedStorageSource(int width, int height); + + void load( + Data::FileOrigin origin, + bool loadFirst, + bool prior) override; + void loadEvenCancelled( + Data::FileOrigin origin, + bool loadFirst, + bool prior) override; + + void setDelayedStorageLocation( + const StorageImageLocation &location) override; + bool isDelayedStorageImage() const override; + void performDelayedLoad(Data::FileOrigin origin) override; + + void automaticLoad( + Data::FileOrigin origin, + const HistoryItem *item) override; // auto load photo + void automaticLoadSettingsChanged() override; + + bool loading() override { + return _location.isNull() ? _loadRequested : StorageSource::loading(); + } + bool displayLoading() override; + void cancel() override; + + +private: + bool _loadRequested = false; + bool _loadCancelled = false; + bool _loadFromCloud = false; + +}; + +class WebUrlSource : public RemoteSource { +public: + // If !box.isEmpty() then resize the image to fit in this box. + explicit WebUrlSource(const QString &url, QSize box = QSize()); + WebUrlSource(const QString &url, int width, int height); + + std::optional cacheKey() override; + + int width() override; + int height() override; + void setInformation(int size, int width, int height) override; + +protected: + QSize shrinkBox() const override; + FileLoader *createLoader( + Data::FileOrigin origin, + LoadFromCloudSetting fromCloud, + bool autoLoading) override; + +private: + QString _url; + QSize _box; + int _size = 0; + int _width = 0; + int _height = 0; + +}; + } // namespace Images class HistoryItem; -class Image { +class Image final { public: - Image(const QString &file, QByteArray format = QByteArray()); - Image(const QByteArray &filecontent, QByteArray format = QByteArray()); - Image(const QPixmap &pixmap, QByteArray format = QByteArray()); - Image(const QByteArray &filecontent, QByteArray format, const QPixmap &pixmap); + explicit Image(std::unique_ptr &&source); static Image *Blank(); - virtual void automaticLoad( - Data::FileOrigin origin, - const HistoryItem *item) { - } - virtual void automaticLoadSettingsChanged() { - } - - virtual bool loaded() const { - return true; - } - virtual bool loading() const { - return false; - } - virtual bool displayLoading() const { - return false; - } - virtual void cancel() { - } - virtual float64 progress() const { - return 1; - } - virtual int32 loadOffset() const { - return 0; - } - const QPixmap &pix( Data::FileOrigin origin, int32 w = 0, @@ -120,285 +439,85 @@ public: int32 w, int32 h = 0) const; - int32 width() const { - return qMax(countWidth(), 1); + void automaticLoad(Data::FileOrigin origin, const HistoryItem *item) { + if (!loaded()) { + _source->automaticLoad(origin, item); + } } - - int32 height() const { - return qMax(countHeight(), 1); + void automaticLoadSettingsChanged() { + _source->automaticLoadSettingsChanged(); } - - virtual void load( - Data::FileOrigin origin, - bool loadFirst = false, - bool prior = true) { + bool loading() const { + return _source->loading(); } - - virtual void loadEvenCancelled( - Data::FileOrigin origin, - bool loadFirst = false, - bool prior = true) { + bool displayLoading() const { + return _source->displayLoading(); } - - virtual const StorageImageLocation &location() const { - return StorageImageLocation::Null; + void cancel() { + _source->cancel(); } - virtual std::optional cacheKey() const; - - bool isNull() const; - - void forget() const; - - QByteArray savedFormat() const { - return _format; + float64 progress() const { + return loaded() ? 1. : _source->progress(); } - QByteArray savedData() const { - return _saved; + int loadOffset() const { + return _source->loadOffset(); } - - virtual void setDelayedStorageLocation( - Data::FileOrigin origin, - const StorageImageLocation location) { - }; - virtual bool isDelayedStorageImage() const { - return false; + int width() const { + return _source->width(); } - - virtual ~Image(); - -protected: - Image(QByteArray format = "PNG") : _format(format) { + int height() const { + return _source->height(); } - - void restore() const; - virtual void checkload() const { + void setInformation(int size, int width, int height) { + _source->setInformation(size, width, height); } - void invalidateSizeCache() const; - - virtual int32 countWidth() const { - restore(); - return _data.width(); - } - - virtual int32 countHeight() const { - restore(); - return _data.height(); - } - - mutable QByteArray _saved, _format; - mutable bool _forgot = false; - mutable QPixmap _data; - -private: - using Sizes = QMap; - mutable Sizes _sizesCache; - -}; - -class RemoteImage : public Image { -public: - void automaticLoad( - Data::FileOrigin origin, - const HistoryItem *item) override; // auto load photo - void automaticLoadSettingsChanged() override; - - bool loaded() const override; - bool loading() const override { - return amLoading(); - } - bool displayLoading() const override; - void cancel() override; - float64 progress() const override; - int32 loadOffset() const override; - - void setImageBytes( - const QByteArray &bytes, - const QByteArray &format = QByteArray()); - void load( - Data::FileOrigin origin, - bool loadFirst = false, - bool prior = true) override; + Data::FileOrigin origin, + bool loadFirst = false, + bool prior = true) { + if (!loaded()) { + _source->load(origin, loadFirst, prior); + } + } void loadEvenCancelled( - Data::FileOrigin origin, - bool loadFirst = false, - bool prior = true) override; - - ~RemoteImage(); - -protected: - // If after loading the image we need to shrink it to fit into a - // specific size, you can return this size here. - virtual QSize shrinkBox() const { - return QSize(); + Data::FileOrigin origin, + bool loadFirst = false, + bool prior = true) { + if (!loaded()) { + _source->loadEvenCancelled(origin, loadFirst, prior); + } } - virtual void setInformation(int32 size, int32 width, int32 height) = 0; - virtual FileLoader *createLoader( - Data::FileOrigin origin, - LoadFromCloudSetting fromCloud, - bool autoLoading) = 0; - - void checkload() const override { - doCheckload(); + const StorageImageLocation &location() const { + return _source->location(); } - void loadLocal(); - -private: - mutable FileLoader *_loader = nullptr; - bool amLoading() const; - void doCheckload() const; - - void destroyLoaderDelayed(FileLoader *newValue = nullptr) const; - -}; - -class StorageImage : public RemoteImage { -public: - explicit StorageImage(const StorageImageLocation &location, int32 size = 0); - StorageImage(const StorageImageLocation &location, const QByteArray &bytes); - - const StorageImageLocation &location() const override { - return _location; - } - std::optional cacheKey() const override; void refreshFileReference(const QByteArray &data) { - _location.refreshFileReference(data); + _source->refreshFileReference(data); + } + std::optional cacheKey() const; + QByteArray bytesForCache() const { + return _source->bytesForCache(); + } + bool isDelayedStorageImage() const { + return _source->isDelayedStorageImage(); } -protected: - void setInformation(int32 size, int32 width, int32 height) override; - FileLoader *createLoader( - Data::FileOrigin origin, - LoadFromCloudSetting fromCloud, - bool autoLoading) override; - - int32 countWidth() const override; - int32 countHeight() const override; - - StorageImageLocation _location; - int32 _size; - -}; - -class WebFileImage : public RemoteImage { -public: - WebFileImage(const WebFileLocation &location, QSize box, int size = 0); - WebFileImage( - const WebFileLocation &location, - int width, - int height, - int size = 0); - - std::optional cacheKey() const override; - -protected: - void setInformation(int size, int width, int height) override; - FileLoader *createLoader( - Data::FileOrigin origin, - LoadFromCloudSetting fromCloud, - bool autoLoading) override; - - QSize shrinkBox() const override { - return _box; - } - - int countWidth() const override; - int countHeight() const override; - - WebFileLocation _location; - QSize _box; - int _width = 0; - int _height = 0; - int _size = 0; - -}; - -class GeoPointImage : public RemoteImage { -public: - GeoPointImage(const GeoPointLocation &location); - - std::optional cacheKey() const override; - -protected: - void setInformation(int size, int width, int height) override; - FileLoader *createLoader( - Data::FileOrigin origin, - LoadFromCloudSetting fromCloud, - bool autoLoading) override; - - int countWidth() const override; - int countHeight() const override; - - GeoPointLocation _location; - int _size = 0; - -}; - -class DelayedStorageImage : public StorageImage { -public: - DelayedStorageImage(); - DelayedStorageImage(int32 w, int32 h); - //DelayedStorageImage(QByteArray &bytes); - + bool loaded() const; + bool isNull() const; + void forget() const; void setDelayedStorageLocation( Data::FileOrigin origin, - const StorageImageLocation location) override; - bool isDelayedStorageImage() const override { - return true; - } + const StorageImageLocation &location); + void setImageBytes(const QByteArray &bytes); - void automaticLoad( - Data::FileOrigin origin, - const HistoryItem *item) override; // auto load photo - void automaticLoadSettingsChanged() override; - - bool loading() const override { - return _location.isNull() ? _loadRequested : StorageImage::loading(); - } - bool displayLoading() const override; - void cancel() override; - - void load( - Data::FileOrigin origin, - bool loadFirst = false, - bool prior = true) override; - void loadEvenCancelled( - Data::FileOrigin origin, - bool loadFirst = false, - bool prior = true) override; + ~Image(); private: - bool _loadRequested, _loadCancelled, _loadFromCloud; + void checkSource() const; + void invalidateSizeCache() const; -}; - -class WebImage : public RemoteImage { -public: - // If !box.isEmpty() then resize the image to fit in this box. - WebImage(const QString &url, QSize box = QSize()); - WebImage(const QString &url, int width, int height); - - void setSize(int width, int height); - - std::optional cacheKey() const override; - -protected: - QSize shrinkBox() const override { - return _box; - } - void setInformation(int32 size, int32 width, int32 height) override; - FileLoader *createLoader( - Data::FileOrigin origin, - LoadFromCloudSetting fromCloud, - bool autoLoading) override; - - int32 countWidth() const override; - int32 countHeight() const override; - -private: - QString _url; - QSize _box; - int32 _size, _width, _height; + std::unique_ptr _source; + mutable QMap _sizesCache; + mutable QImage _data; }; @@ -409,28 +528,28 @@ Image *Create(const QString &file, QByteArray format); Image *Create(const QString &url, QSize box); Image *Create(const QString &url, int width, int height); Image *Create(const QByteArray &filecontent, QByteArray format); -Image *Create(const QPixmap &pixmap, QByteArray format); +Image *Create(QImage &&data, QByteArray format); Image *Create( const QByteArray &filecontent, QByteArray format, - const QPixmap &pixmap); -Image *Create(int32 width, int32 height); -StorageImage *Create(const StorageImageLocation &location, int size = 0); -StorageImage *Create( // photoCachedSize + QImage &&data); +Image *Create(int width, int height); +Image *Create(const StorageImageLocation &location, int size = 0); +Image *Create( // photoCachedSize const StorageImageLocation &location, const QByteArray &bytes); Image *Create(const MTPWebDocument &location); Image *Create(const MTPWebDocument &location, QSize box); -WebFileImage *Create( +Image *Create( const WebFileLocation &location, int width, int height, int size = 0); -WebFileImage *Create( +Image *Create( const WebFileLocation &location, QSize box, int size = 0); -GeoPointImage *Create( +Image *Create( const GeoPointLocation &location); } // namespace details diff --git a/Telegram/SourceFiles/ui/images.cpp b/Telegram/SourceFiles/ui/images.cpp index 1d672e535..d203aa2a6 100644 --- a/Telegram/SourceFiles/ui/images.cpp +++ b/Telegram/SourceFiles/ui/images.cpp @@ -389,12 +389,12 @@ ImagePtr::ImagePtr(const QByteArray &filecontent, QByteArray format) ImagePtr::ImagePtr( const QByteArray &filecontent, QByteArray format, - const QPixmap &pixmap) -: _data(Images::details::Create(filecontent, format, pixmap)) { + QImage &&data) +: _data(Images::details::Create(filecontent, format, std::move(data))) { } -ImagePtr::ImagePtr(const QPixmap &pixmap, QByteArray format) -: _data(Images::details::Create(pixmap, format)) { +ImagePtr::ImagePtr(QImage &&data, QByteArray format) +: _data(Images::details::Create(std::move(data), format)) { } ImagePtr::ImagePtr(const StorageImageLocation &location, int32 size) diff --git a/Telegram/SourceFiles/ui/images.h b/Telegram/SourceFiles/ui/images.h index ce57d63ba..a420be3e3 100644 --- a/Telegram/SourceFiles/ui/images.h +++ b/Telegram/SourceFiles/ui/images.h @@ -287,8 +287,8 @@ public: ImagePtr(const QString &url, QSize box); ImagePtr(const QString &url, int width, int height); ImagePtr(const QByteArray &filecontent, QByteArray format = QByteArray()); - ImagePtr(const QByteArray &filecontent, QByteArray format, const QPixmap &pixmap); - ImagePtr(const QPixmap &pixmap, QByteArray format); + ImagePtr(const QByteArray &filecontent, QByteArray format, QImage &&data); + ImagePtr(QImage &&data, QByteArray format); ImagePtr(const StorageImageLocation &location, int32 size = 0); ImagePtr(const StorageImageLocation &location, const QByteArray &bytes); ImagePtr(const MTPWebDocument &location);