diff --git a/Telegram/CMakeLists.txt b/Telegram/CMakeLists.txt index fd33b51a3..fc56207bc 100644 --- a/Telegram/CMakeLists.txt +++ b/Telegram/CMakeLists.txt @@ -393,6 +393,8 @@ PRIVATE data/data_peer_values.h data/data_photo.cpp data/data_photo.h + data/data_photo_media.cpp + data/data_photo_media.h data/data_poll.cpp data/data_poll.h data/data_pts_waiter.cpp diff --git a/Telegram/SourceFiles/boxes/confirm_box.cpp b/Telegram/SourceFiles/boxes/confirm_box.cpp index 4839c5269..34be632f2 100644 --- a/Telegram/SourceFiles/boxes/confirm_box.cpp +++ b/Telegram/SourceFiles/boxes/confirm_box.cpp @@ -32,6 +32,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "data/data_user.h" #include "data/data_file_origin.h" #include "data/data_histories.h" +#include "data/data_photo_media.h" #include "base/unixtime.h" #include "main/main_session.h" #include "observer_peer.h" @@ -894,12 +895,12 @@ ConfirmInviteBox::ConfirmInviteBox( const auto photo = _session->data().processPhoto(data.vphoto()); if (!photo->isNull()) { - _photo = photo->thumbnail(); - if (!_photo->loaded()) { + _photo = photo->createMediaView(); + _photo->wanted(Data::PhotoSize::Small, Data::FileOrigin()); + if (!_photo->image(Data::PhotoSize::Small)) { subscribe(_session->downloaderTaskFinished(), [=] { update(); }); - _photo->load(Data::FileOrigin()); } } else { _photoEmpty = std::make_unique<Ui::EmptyUserpic>( @@ -972,14 +973,16 @@ void ConfirmInviteBox::paintEvent(QPaintEvent *e) { Painter p(this); if (_photo) { - p.drawPixmap( - (width() - st::confirmInvitePhotoSize) / 2, - st::confirmInvitePhotoTop, - _photo->pixCircled( - Data::FileOrigin(), - st::confirmInvitePhotoSize, - st::confirmInvitePhotoSize)); - } else { + if (const auto image = _photo->image(Data::PhotoSize::Small)) { + p.drawPixmap( + (width() - st::confirmInvitePhotoSize) / 2, + st::confirmInvitePhotoTop, + image->pixCircled( + Data::FileOrigin(), + st::confirmInvitePhotoSize, + st::confirmInvitePhotoSize)); + } + } else if (_photoEmpty) { _photoEmpty->paint( p, (width() - st::confirmInvitePhotoSize) / 2, diff --git a/Telegram/SourceFiles/boxes/confirm_box.h b/Telegram/SourceFiles/boxes/confirm_box.h index d3c53edda..daf242b16 100644 --- a/Telegram/SourceFiles/boxes/confirm_box.h +++ b/Telegram/SourceFiles/boxes/confirm_box.h @@ -10,6 +10,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "boxes/abstract_box.h" #include "mtproto/mtproto_rpc_sender.h" +namespace Data { +class PhotoMedia; +} // namespace Data + namespace Main { class Session; } // namespace Main @@ -230,7 +234,7 @@ private: Fn<void()> _submit; object_ptr<Ui::FlatLabel> _title; object_ptr<Ui::FlatLabel> _status; - Image *_photo = nullptr; + std::shared_ptr<Data::PhotoMedia> _photo; std::unique_ptr<Ui::EmptyUserpic> _photoEmpty; std::vector<not_null<UserData*>> _participants; bool _isChannel = false; diff --git a/Telegram/SourceFiles/boxes/edit_caption_box.cpp b/Telegram/SourceFiles/boxes/edit_caption_box.cpp index 115d247f7..28af6957f 100644 --- a/Telegram/SourceFiles/boxes/edit_caption_box.cpp +++ b/Telegram/SourceFiles/boxes/edit_caption_box.cpp @@ -24,6 +24,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "data/data_session.h" #include "data/data_streaming.h" #include "data/data_file_origin.h" +#include "data/data_photo_media.h" #include "data/data_document_media.h" #include "history/history.h" #include "history/history_item.h" @@ -55,6 +56,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL namespace { using namespace ::Media::Streaming; +using Data::PhotoSize; } // namespace @@ -75,9 +77,10 @@ EditCaptionBox::EditCaptionBox( const auto media = item->media(); if (const auto photo = media->photo()) { - _photo = true; - dimensions = QSize(photo->width(), photo->height()); - image = photo->large(); + _photoMedia = photo->createMediaView(); + _photoMedia->wanted(PhotoSize::Large, _msgId); + image = _photoMedia->image(PhotoSize::Large); + dimensions = _photoMedia->size(PhotoSize::Large); } else if (const auto document = media->document()) { _documentMedia = document->createMediaView(); _documentMedia->thumbnailWanted(_msgId); @@ -97,7 +100,10 @@ EditCaptionBox::EditCaptionBox( } const auto editData = PrepareEditText(item); - if (!_animated && (dimensions.isEmpty() || _documentMedia || !image)) { + if (!_animated + && (dimensions.isEmpty() + || _documentMedia + || (!_photoMedia && !image))) { if (!image) { _thumbw = 0; } else { @@ -139,7 +145,7 @@ EditCaptionBox::EditCaptionBox( _refreshThumbnail(); } } else { - if (!image) { + if (!image && !_photoMedia) { image = Image::BlankMedia(); } auto maxW = 0, maxH = 0; @@ -177,7 +183,10 @@ EditCaptionBox::EditCaptionBox( maxH = dimensions.height(); _thumbnailImage = image; _refreshThumbnail = [=] { - _thumb = image->pixNoCache( + if (!_thumbnailImage) { + return; + } + _thumb = _thumbnailImage->pixNoCache( _msgId, maxW * cIntRetinaFactor(), maxH * cIntRetinaFactor(), @@ -253,6 +262,8 @@ EditCaptionBox::EditCaptionBox( _thumbnailImageLoaded = _thumbnailImage ? _thumbnailImage->loaded() + : _photoMedia + ? false : _documentMedia ? !_documentMedia->owner()->hasThumbnail() : true; @@ -260,6 +271,8 @@ EditCaptionBox::EditCaptionBox( subscribe(_controller->session().downloaderTaskFinished(), [=] { if (_thumbnailImageLoaded) { return; + } else if (!_thumbnailImage && _photoMedia) { + _thumbnailImage = _photoMedia->image(PhotoSize::Large); } else if (!_thumbnailImage && _documentMedia && _documentMedia->owner()->hasThumbnail()) { diff --git a/Telegram/SourceFiles/boxes/edit_caption_box.h b/Telegram/SourceFiles/boxes/edit_caption_box.h index bf83e3323..7cecf946f 100644 --- a/Telegram/SourceFiles/boxes/edit_caption_box.h +++ b/Telegram/SourceFiles/boxes/edit_caption_box.h @@ -22,6 +22,7 @@ class SessionController; namespace Data { class Media; +class PhotoMedia; class DocumentMedia; } // namespace Data @@ -103,6 +104,7 @@ private: not_null<Window::SessionController*> _controller; FullMsgId _msgId; + std::shared_ptr<Data::PhotoMedia> _photoMedia; std::shared_ptr<Data::DocumentMedia> _documentMedia; Image *_thumbnailImage = nullptr; bool _thumbnailImageLoaded = false; diff --git a/Telegram/SourceFiles/calls/calls_panel.cpp b/Telegram/SourceFiles/calls/calls_panel.cpp index 59e7bc53b..b13ad2db6 100644 --- a/Telegram/SourceFiles/calls/calls_panel.cpp +++ b/Telegram/SourceFiles/calls/calls_panel.cpp @@ -11,6 +11,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "data/data_session.h" #include "data/data_user.h" #include "data/data_file_origin.h" +#include "data/data_photo_media.h" #include "calls/calls_emoji_fingerprint.h" #include "ui/widgets/buttons.h" #include "ui/widgets/labels.h" @@ -299,7 +300,7 @@ QImage Panel::Button::prepareRippleMask() const { } Panel::Panel(not_null<Call*> call) -:RpWidget(App::wnd()) +: RpWidget(App::wnd()) , _call(call) , _user(call->user()) , _answerHangupRedial(this, st::callAnswer, &st::callHangup) @@ -319,6 +320,8 @@ Panel::Panel(not_null<Call*> call) showAndActivate(); } +Panel::~Panel() = default; + void Panel::showAndActivate() { toggleOpacityAnimation(true); raise(); @@ -515,26 +518,28 @@ void Panel::processUserPhoto() { ? _user->owner().photo(_user->userpicPhotoId()).get() : nullptr; if (isGoodUserPhoto(photo)) { - photo->large()->load(_user->userpicPhotoOrigin()); - } else if (_user->userpicPhotoUnknown() || (photo && !photo->date)) { - _user->session().api().requestFullPeer(_user); + _photo = photo->createMediaView(); + _photo->wanted(Data::PhotoSize::Large, _user->userpicPhotoOrigin()); + } else { + _photo = nullptr; + if (_user->userpicPhotoUnknown() || (photo && !photo->date)) { + _user->session().api().requestFullPeer(_user); + } } refreshUserPhoto(); } void Panel::refreshUserPhoto() { - const auto photo = _user->userpicPhotoId() - ? _user->owner().photo(_user->userpicPhotoId()).get() - : nullptr; - const auto isNewPhoto = [&](not_null<PhotoData*> photo) { - return photo->large()->loaded() - && (photo->id != _userPhotoId || !_userPhotoFull); - }; - if (isGoodUserPhoto(photo) && isNewPhoto(photo)) { - _userPhotoId = photo->id; + const auto isNewBigPhoto = [&] { + return _photo + && _photo->loaded() + && (_photo->owner()->id != _userPhotoId || !_userPhotoFull); + }(); + if (isNewBigPhoto) { + _userPhotoId = _photo->owner()->id; _userPhotoFull = true; createUserpicCache( - photo->isNull() ? nullptr : photo->large().get(), + _photo->image(Data::PhotoSize::Large), _user->userpicPhotoOrigin()); } else if (_userPhoto.isNull()) { const auto userpic = _user->currentUserpic(); diff --git a/Telegram/SourceFiles/calls/calls_panel.h b/Telegram/SourceFiles/calls/calls_panel.h index c7ba8feee..518d20225 100644 --- a/Telegram/SourceFiles/calls/calls_panel.h +++ b/Telegram/SourceFiles/calls/calls_panel.h @@ -14,6 +14,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "ui/effects/animations.h" #include "ui/rp_widget.h" +namespace Data { +class PhotoMedia; +} // namespace Data + namespace Ui { class IconButton; class FlatLabel; @@ -56,6 +60,7 @@ class Panel public: Panel(not_null<Call*> call); + ~Panel(); void showAndActivate(); void replaceCall(not_null<Call*> call); @@ -111,6 +116,7 @@ private: Call *_call = nullptr; not_null<UserData*> _user; + std::shared_ptr<Data::PhotoMedia> _photo; bool _useTransparency = true; style::margins _padding; diff --git a/Telegram/SourceFiles/chat_helpers/gifs_list_widget.cpp b/Telegram/SourceFiles/chat_helpers/gifs_list_widget.cpp index c181144e4..5398d74c4 100644 --- a/Telegram/SourceFiles/chat_helpers/gifs_list_widget.cpp +++ b/Telegram/SourceFiles/chat_helpers/gifs_list_widget.cpp @@ -13,6 +13,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "data/data_session.h" #include "data/data_user.h" #include "data/data_file_origin.h" +#include "data/data_photo_media.h" #include "data/data_document_media.h" #include "ui/widgets/buttons.h" #include "ui/widgets/input_fields.h" @@ -370,18 +371,23 @@ void GifsListWidget::selectInlineResult(int row, int column) { return; } + const auto ctrl = (QGuiApplication::keyboardModifiers() + == Qt::ControlModifier); auto item = _rows[row].items[column]; if (const auto photo = item->getPhoto()) { - if (photo->thumbnail()->loaded()) { + using Data::PhotoSize; + const auto media = photo->activeMediaView(); + if (ctrl + || (media && media->image(PhotoSize::Thumbnail)) + || (media && media->image(PhotoSize::Large))) { _photoChosen.fire_copy(photo); - } else if (!photo->thumbnail()->loading()) { - photo->thumbnail()->loadEvenCancelled(Data::FileOrigin()); + } else if (!photo->loading(PhotoSize::Thumbnail)) { + photo->load(PhotoSize::Thumbnail, Data::FileOrigin()); } } else if (const auto document = item->getDocument()) { const auto media = document->activeMediaView(); const auto preview = Data::VideoPreviewState(media.get()); - if ((media && preview.loaded()) - || QGuiApplication::keyboardModifiers() == Qt::ControlModifier) { + if (ctrl || (media && preview.loaded())) { _fileChosen.fire_copy(document); } else if (!preview.usingThumbnail()) { if (preview.loading()) { @@ -551,11 +557,13 @@ void GifsListWidget::clearInlineRows(bool resultsDeleted) { _rows.clear(); } -GifsListWidget::LayoutItem *GifsListWidget::layoutPrepareSavedGif(DocumentData *doc, int32 position) { - auto it = _gifLayouts.find(doc); +GifsListWidget::LayoutItem *GifsListWidget::layoutPrepareSavedGif( + not_null<DocumentData*> document, + int32 position) { + auto it = _gifLayouts.find(document); if (it == _gifLayouts.cend()) { - if (auto layout = LayoutItem::createLayoutGif(this, doc)) { - it = _gifLayouts.emplace(doc, std::move(layout)).first; + if (auto layout = LayoutItem::createLayoutGif(this, document)) { + it = _gifLayouts.emplace(document, std::move(layout)).first; it->second->initDimensions(); } else { return nullptr; @@ -567,7 +575,9 @@ GifsListWidget::LayoutItem *GifsListWidget::layoutPrepareSavedGif(DocumentData * return it->second.get(); } -GifsListWidget::LayoutItem *GifsListWidget::layoutPrepareInlineResult(InlineResult *result, int32 position) { +GifsListWidget::LayoutItem *GifsListWidget::layoutPrepareInlineResult( + not_null<InlineResult*> result, + int32 position) { auto it = _inlineLayouts.find(result); if (it == _inlineLayouts.cend()) { if (auto layout = LayoutItem::createLayout(this, result, _inlineWithThumb)) { diff --git a/Telegram/SourceFiles/chat_helpers/gifs_list_widget.h b/Telegram/SourceFiles/chat_helpers/gifs_list_widget.h index 49f1b5797..7a67388b3 100644 --- a/Telegram/SourceFiles/chat_helpers/gifs_list_widget.h +++ b/Telegram/SourceFiles/chat_helpers/gifs_list_widget.h @@ -134,11 +134,19 @@ private: QVector<Row> _rows; void clearInlineRows(bool resultsDeleted); - std::map<DocumentData*, std::unique_ptr<LayoutItem>> _gifLayouts; - LayoutItem *layoutPrepareSavedGif(DocumentData *doc, int32 position); + std::map< + not_null<DocumentData*>, + std::unique_ptr<LayoutItem>> _gifLayouts; + LayoutItem *layoutPrepareSavedGif( + not_null<DocumentData*> document, + int32 position); - std::map<InlineResult*, std::unique_ptr<LayoutItem>> _inlineLayouts; - LayoutItem *layoutPrepareInlineResult(InlineResult *result, int32 position); + std::map< + not_null<InlineResult*>, + std::unique_ptr<LayoutItem>> _inlineLayouts; + LayoutItem *layoutPrepareInlineResult( + not_null<InlineResult*> result, + int32 position); bool inlineRowsAddItem(DocumentData *savedGif, InlineResult *result, Row &row, int32 &sumWidth); bool inlineRowFinalize(Row &row, int32 &sumWidth, bool force = false); diff --git a/Telegram/SourceFiles/chat_helpers/stickers_emoji_pack.cpp b/Telegram/SourceFiles/chat_helpers/stickers_emoji_pack.cpp index 4a60341f4..51b0bb31d 100644 --- a/Telegram/SourceFiles/chat_helpers/stickers_emoji_pack.cpp +++ b/Telegram/SourceFiles/chat_helpers/stickers_emoji_pack.cpp @@ -128,11 +128,6 @@ public: QImage takeLoaded() override; void unload() override; - void automaticLoad( - Data::FileOrigin origin, - const HistoryItem *item) override; - void automaticLoadSettingsChanged() override; - bool loading() override; bool displayLoading() override; void cancel() override; @@ -222,14 +217,6 @@ void ImageSource::unload() { _data = QImage(); } -void ImageSource::automaticLoad( - Data::FileOrigin origin, - const HistoryItem *item) { -} - -void ImageSource::automaticLoadSettingsChanged() { -} - bool ImageSource::loading() { return _data.isNull() && _bytes.isEmpty(); } diff --git a/Telegram/SourceFiles/data/data_auto_download.cpp b/Telegram/SourceFiles/data/data_auto_download.cpp index 8dcf882fb..db9d863c9 100644 --- a/Telegram/SourceFiles/data/data_auto_download.cpp +++ b/Telegram/SourceFiles/data/data_auto_download.cpp @@ -10,8 +10,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "data/data_peer.h" #include "data/data_photo.h" #include "data/data_document.h" -#include "ui/image/image_source.h" -#include "ui/image/image.h" #include <QtCore/QBuffer> @@ -294,11 +292,11 @@ bool Should( bool Should( const Full &data, not_null<PeerData*> peer, - not_null<Images::Source*> image) { + not_null<PhotoData*> photo) { return data.shouldDownload( SourceFromPeer(peer), Type::Photo, - image->bytesSize()); + photo->imageByteSize(PhotoSize::Large)); } bool ShouldAutoPlay( diff --git a/Telegram/SourceFiles/data/data_auto_download.h b/Telegram/SourceFiles/data/data_auto_download.h index 7da31d8bd..ab2a6fe4e 100644 --- a/Telegram/SourceFiles/data/data_auto_download.h +++ b/Telegram/SourceFiles/data/data_auto_download.h @@ -9,10 +9,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include <array> -namespace Images { -class Source; -} // namespace Images - namespace Data { namespace AutoDownload { @@ -118,7 +114,7 @@ private: [[nodiscard]] bool Should( const Full &data, not_null<PeerData*> peer, - not_null<Images::Source*> image); + not_null<PhotoData*> photo); [[nodiscard]] bool ShouldAutoPlay( const Full &data, diff --git a/Telegram/SourceFiles/data/data_document.cpp b/Telegram/SourceFiles/data/data_document.cpp index 7f61a9716..7d88f0378 100644 --- a/Telegram/SourceFiles/data/data_document.cpp +++ b/Telegram/SourceFiles/data/data_document.cpp @@ -700,10 +700,10 @@ void DocumentData::loadThumbnail(Data::FileOrigin origin) { _flags |= Flag::ThumbnailFailed; }, [=] { if (_thumbnailLoader && !_thumbnailLoader->cancelled()) { - if (auto image = _thumbnailLoader->imageData(); image.isNull()) { + if (auto read = _thumbnailLoader->imageData(); read.isNull()) { _flags |= Flag::ThumbnailFailed; } else if (const auto active = activeMediaView()) { - active->setThumbnail(std::move(image)); + active->setThumbnail(std::move(read)); } } _thumbnailLoader = nullptr; diff --git a/Telegram/SourceFiles/data/data_document.h b/Telegram/SourceFiles/data/data_document.h index e7bf68c2d..369a6b622 100644 --- a/Telegram/SourceFiles/data/data_document.h +++ b/Telegram/SourceFiles/data/data_document.h @@ -159,8 +159,8 @@ public: [[nodiscard]] bool thumbnailLoading() const; [[nodiscard]] bool thumbnailFailed() const; void loadThumbnail(Data::FileOrigin origin); - const ImageLocation &thumbnailLocation() const; - int thumbnailByteSize() const; + [[nodiscard]] const ImageLocation &thumbnailLocation() const; + [[nodiscard]] int thumbnailByteSize() const; [[nodiscard]] bool hasVideoThumbnail() const; [[nodiscard]] bool videoThumbnailLoading() const; diff --git a/Telegram/SourceFiles/data/data_media_types.cpp b/Telegram/SourceFiles/data/data_media_types.cpp index 16e3ab789..c10b98b82 100644 --- a/Telegram/SourceFiles/data/data_media_types.cpp +++ b/Telegram/SourceFiles/data/data_media_types.cpp @@ -381,98 +381,98 @@ bool MediaPhoto::updateSentMedia(const MTPMessageMedia &media) { } parent()->history()->owner().photoConvert(_photo, *content); - if (content->type() != mtpc_photo) { - return false; - } - const auto &photo = content->c_photo(); + //if (content->type() != mtpc_photo) { // #TODO optimize + // return false; + //} + //const auto &photo = content->c_photo(); - struct SizeData { - MTPstring type = MTP_string(); - int width = 0; - int height = 0; - QByteArray bytes; - }; - const auto saveImageToCache = [&]( - not_null<Image*> image, - SizeData size) { - Expects(!size.type.v.isEmpty()); + //struct SizeData { + // MTPstring type = MTP_string(); + // int width = 0; + // int height = 0; + // QByteArray bytes; + //}; + //const auto saveImageToCache = [&]( + // not_null<Image*> image, + // SizeData size) { + // Expects(!size.type.v.isEmpty()); - const auto key = StorageImageLocation( - StorageFileLocation( - photo.vdc_id().v, - _photo->session().userId(), - MTP_inputPhotoFileLocation( - photo.vid(), - photo.vaccess_hash(), - photo.vfile_reference(), - size.type)), - size.width, - size.height); - if (!key.valid() || image->isNull() || !image->loaded()) { - return; - } - if (size.bytes.isEmpty()) { - size.bytes = image->bytesForCache(); - } - const auto length = size.bytes.size(); - if (!length || length > Storage::kMaxFileInMemory) { - LOG(("App Error: Bad photo data for saving to cache.")); - return; - } - parent()->history()->owner().cache().putIfEmpty( - key.file().cacheKey(), - Storage::Cache::Database::TaggedValue( - std::move(size.bytes), - Data::kImageCacheTag)); - image->replaceSource( - std::make_unique<Images::StorageSource>(key, length)); - }; - auto &sizes = photo.vsizes().v; - auto max = 0; - auto maxSize = SizeData(); - for (const auto &data : sizes) { - const auto size = data.match([](const MTPDphotoSize &data) { - return SizeData{ - data.vtype(), - data.vw().v, - data.vh().v, - QByteArray() - }; - }, [](const MTPDphotoCachedSize &data) { - return SizeData{ - data.vtype(), - data.vw().v, - data.vh().v, - qba(data.vbytes()) - }; - }, [](const MTPDphotoSizeEmpty &) { - return SizeData(); - }, [](const MTPDphotoStrippedSize &data) { - // No need to save stripped images to local cache. - return SizeData(); - }); - const auto letter = size.type.v.isEmpty() ? char(0) : size.type.v[0]; - if (!letter) { - continue; - } - if (letter == 's') { - saveImageToCache(_photo->thumbnailSmall(), size); - } else if (letter == 'm') { - saveImageToCache(_photo->thumbnail(), size); - } else if (letter == 'x' && max < 1) { - max = 1; - maxSize = size; - } else if (letter == 'y' && max < 2) { - max = 2; - maxSize = size; - //} else if (letter == 'w' && max < 3) { - // max = 3; - // maxSize = size; - } - } - if (!maxSize.type.v.isEmpty()) { - saveImageToCache(_photo->large(), maxSize); - } + // const auto key = StorageImageLocation( + // StorageFileLocation( + // photo.vdc_id().v, + // _photo->session().userId(), + // MTP_inputPhotoFileLocation( + // photo.vid(), + // photo.vaccess_hash(), + // photo.vfile_reference(), + // size.type)), + // size.width, + // size.height); + // if (!key.valid() || image->isNull() || !image->loaded()) { + // return; + // } + // if (size.bytes.isEmpty()) { + // size.bytes = image->bytesForCache(); + // } + // const auto length = size.bytes.size(); + // if (!length || length > Storage::kMaxFileInMemory) { + // LOG(("App Error: Bad photo data for saving to cache.")); + // return; + // } + // parent()->history()->owner().cache().putIfEmpty( + // key.file().cacheKey(), + // Storage::Cache::Database::TaggedValue( + // std::move(size.bytes), + // Data::kImageCacheTag)); + // image->replaceSource( + // std::make_unique<Images::StorageSource>(key, length)); + //}; + //auto &sizes = photo.vsizes().v; + //auto max = 0; + //auto maxSize = SizeData(); + //for (const auto &data : sizes) { + // const auto size = data.match([](const MTPDphotoSize &data) { + // return SizeData{ + // data.vtype(), + // data.vw().v, + // data.vh().v, + // QByteArray() + // }; + // }, [](const MTPDphotoCachedSize &data) { + // return SizeData{ + // data.vtype(), + // data.vw().v, + // data.vh().v, + // qba(data.vbytes()) + // }; + // }, [](const MTPDphotoSizeEmpty &) { + // return SizeData(); + // }, [](const MTPDphotoStrippedSize &data) { + // // No need to save stripped images to local cache. + // return SizeData(); + // }); + // const auto letter = size.type.v.isEmpty() ? char(0) : size.type.v[0]; + // if (!letter) { + // continue; + // } + // if (letter == 's') { + // saveImageToCache(_photo->thumbnailSmall(), size); + // } else if (letter == 'm') { + // saveImageToCache(_photo->thumbnail(), size); + // } else if (letter == 'x' && max < 1) { + // max = 1; + // maxSize = size; + // } else if (letter == 'y' && max < 2) { + // max = 2; + // maxSize = size; + // //} else if (letter == 'w' && max < 3) { + // // max = 3; + // // maxSize = size; + // } + //} + //if (!maxSize.type.v.isEmpty()) { + // saveImageToCache(_photo->large(), maxSize); + //} return true; } diff --git a/Telegram/SourceFiles/data/data_photo.cpp b/Telegram/SourceFiles/data/data_photo.cpp index 4905b2d39..7c0c872a5 100644 --- a/Telegram/SourceFiles/data/data_photo.cpp +++ b/Telegram/SourceFiles/data/data_photo.cpp @@ -10,20 +10,35 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "data/data_session.h" #include "data/data_file_origin.h" #include "data/data_reply_preview.h" +#include "data/data_photo_media.h" #include "ui/image/image.h" #include "ui/image/image_source.h" #include "main/main_session.h" #include "mainwidget.h" +#include "storage/file_download.h" #include "core/application.h" #include "facades.h" #include "app.h" +namespace { + +using Data::PhotoMedia; +using Data::PhotoSize; +using Data::PhotoSizeIndex; +using Data::kPhotoSizeCount; + +} // namespace + PhotoData::PhotoData(not_null<Data::Session*> owner, PhotoId id) : id(id) , _owner(owner) { } -PhotoData::~PhotoData() = default; +PhotoData::~PhotoData() { + for (auto &image : _images) { + base::take(image.loader).reset(); + } +} Data::Session &PhotoData::owner() const { return *_owner; @@ -33,55 +48,77 @@ Main::Session &PhotoData::session() const { return _owner->session(); } -void PhotoData::automaticLoad( - Data::FileOrigin origin, - const HistoryItem *item) { - _large->automaticLoad(origin, item); -} - void PhotoData::automaticLoadSettingsChanged() { - _large->automaticLoadSettingsChanged(); -} - -void PhotoData::download(Data::FileOrigin origin) { - _large->loadEvenCancelled(origin); - _owner->notifyPhotoLayoutChanged(this); -} - -bool PhotoData::loaded() const { - bool wasLoading = loading(); - if (_large->loaded()) { - if (wasLoading) { - _owner->notifyPhotoLayoutChanged(this); - } - return true; + const auto index = PhotoSizeIndex(PhotoSize::Large); + if (!(_images[index].flags & ImageFlag::Cancelled)) { + return; } - return false; + _images[index].loader = nullptr; + _images[index].flags &= ~ImageFlag::Cancelled; +} + +void PhotoData::load( + Data::FileOrigin origin, + LoadFromCloudSetting fromCloud, + bool autoLoading) { + load(PhotoSize::Large, origin, fromCloud, autoLoading); } bool PhotoData::loading() const { - return _large->loading(); + return loading(PhotoSize::Large); +} + +bool PhotoData::loading(PhotoSize size) const { + return (_images[PhotoSizeIndex(size)].loader != nullptr); +} + +bool PhotoData::failed(PhotoSize size) const { + return (_images[PhotoSizeIndex(size)].flags & ImageFlag::Failed); +} + +const ImageLocation &PhotoData::location(PhotoSize size) const { + return _images[PhotoSizeIndex(size)].location; +} + +int PhotoData::imageByteSize(PhotoSize size) const { + return _images[PhotoSizeIndex(size)].byteSize; } bool PhotoData::displayLoading() const { - return _large->loading() - ? _large->displayLoading() + const auto index = PhotoSizeIndex(PhotoSize::Large); + return _images[index].loader + ? (!_images[index].loader->loadingLocal() + || !_images[index].loader->autoLoading()) : (uploading() && !waitingForAlbum()); } void PhotoData::cancel() { - _large->cancel(); - _owner->notifyPhotoLayoutChanged(this); + if (!loading()) { + return; + } + + const auto index = PhotoSizeIndex(PhotoSize::Large); + _images[index].flags |= ImageFlag::Cancelled; + destroyLoader(PhotoSize::Large); + _owner->photoLoadDone(this); } float64 PhotoData::progress() const { if (uploading()) { if (uploadingData->size > 0) { - return float64(uploadingData->offset) / uploadingData->size; + const auto result = float64(uploadingData->offset) + / uploadingData->size; + return snap(result, 0., 1.); } - return 0; + return 0.; } - return _large->progress(); + const auto index = PhotoSizeIndex(PhotoSize::Large); + return loading() ? _images[index].loader->currentProgress() : 0.; +} + +bool PhotoData::cancelled() const { + const auto index = PhotoSizeIndex(PhotoSize::Large); + return (_images[index].flags & ImageFlag::Cancelled); } void PhotoData::setWaitingForAlbum() { @@ -95,7 +132,8 @@ bool PhotoData::waitingForAlbum() const { } int32 PhotoData::loadOffset() const { - return _large->loadOffset(); + const auto index = PhotoSizeIndex(PhotoSize::Large); + return loading() ? _images[index].loader->currentOffset() : 0; } bool PhotoData::uploading() const { @@ -103,11 +141,6 @@ bool PhotoData::uploading() const { } void PhotoData::unload() { - // Forget thumbnail only when image cache limit exceeds. - //_thumbnailInline->unload(); - _thumbnailSmall->unload(); - _thumbnail->unload(); - _large->unload(); _replyPreview = nullptr; } @@ -142,9 +175,9 @@ QByteArray PhotoData::fileReference() const { void PhotoData::refreshFileReference(const QByteArray &value) { _fileReference = value; - _thumbnailSmall->refreshFileReference(value); - _thumbnail->refreshFileReference(value); - _large->refreshFileReference(value); + for (auto &image : _images) { + image.location.refreshFileReference(value); + } } void PhotoData::collectLocalData(not_null<PhotoData*> local) { @@ -152,83 +185,185 @@ void PhotoData::collectLocalData(not_null<PhotoData*> local) { return; } - const auto copyImage = [&](const ImagePtr &src, const ImagePtr &dst) { - if (const auto from = src->cacheKey()) { - if (const auto to = dst->cacheKey()) { + for (auto i = 0; i != kPhotoSizeCount; ++i) { + if (const auto from = local->_images[i].location.file().cacheKey()) { + if (const auto to = _images[i].location.file().cacheKey()) { _owner->cache().copyIfEmpty(from, to); } } - }; - copyImage(local->_thumbnailSmall, _thumbnailSmall); - copyImage(local->_thumbnail, _thumbnail); - copyImage(local->_large, _large); + } + if (const auto localMedia = local->activeMediaView()) { + const auto media = createMediaView(); + media->collectLocalData(localMedia.get()); + + // Keep DocumentMedia alive for some more time. + // NB! This allows DocumentMedia to outlive Main::Session! + // In case this is a problem this code should be rewritten. + crl::on_main(&session(), [media] {}); + } } bool PhotoData::isNull() const { - return _large->isNull(); + return !_images[PhotoSizeIndex(PhotoSize::Large)].location.valid(); } -void PhotoData::loadThumbnail(Data::FileOrigin origin) { - _thumbnail->load(origin); +void PhotoData::load( + PhotoSize size, + Data::FileOrigin origin, + LoadFromCloudSetting fromCloud, + bool autoLoading) { + const auto index = PhotoSizeIndex(size); + auto &image = _images[index]; + if (image.loader) { + if (fromCloud == LoadFromCloudOrLocal) { + image.loader->permitLoadFromCloud(); + } + return; + } else if ((image.flags & ImageFlag::Failed) + || !image.location.valid()) { + return; + } else if (const auto active = activeMediaView()) { + if (active->image(size)) { + return; + } + } + image.flags &= ~ImageFlag::Cancelled; + image.loader = CreateFileLoader( + image.location.file(), + origin, + QString(), + image.byteSize, + UnknownFileLocation, + LoadToCacheAsWell, + fromCloud, + autoLoading, + Data::kImageCacheTag); + + image.loader->updates( + ) | rpl::start_with_next_error_done([=] { + if (size == PhotoSize::Large) { + _owner->photoLoadProgress(this); + } + }, [=, &image](bool started) { + finishLoad(size); + image.flags |= ImageFlag::Failed; + if (size == PhotoSize::Large) { + _owner->photoLoadFail(this, started); + } + }, [=, &image] { + finishLoad(size); + if (size == PhotoSize::Large) { + _owner->photoLoadDone(this); + } + }, image.loader->lifetime()); + + image.loader->start(); + + if (size == PhotoSize::Large) { + _owner->notifyPhotoLayoutChanged(this); + } } -void PhotoData::loadThumbnailSmall(Data::FileOrigin origin) { - _thumbnailSmall->load(origin); +void PhotoData::finishLoad(PhotoSize size) { + const auto index = PhotoSizeIndex(size); + auto &image = _images[index]; + + // NB! image.loader may be in ~FileLoader() already. + const auto guard = gsl::finally([&] { + destroyLoader(size); + }); + if (!image.loader || image.loader->cancelled()) { + image.flags |= ImageFlag::Cancelled; + return; + } else if (auto read = image.loader->imageData(); read.isNull()) { + image.flags |= ImageFlag::Failed; + } else if (const auto active = activeMediaView()) { + active->set(size, std::move(read)); + } } -Image *PhotoData::thumbnailInline() const { - return _thumbnailInline ? _thumbnailInline.get() : nullptr; +void PhotoData::destroyLoader(PhotoSize size) { + const auto index = PhotoSizeIndex(size); + auto &image = _images[index]; + + // NB! image.loader may be in ~FileLoader() already. + if (!image.loader) { + return; + } + const auto loader = base::take(image.loader); + if (image.flags & ImageFlag::Cancelled) { + loader->cancel(); + } } -not_null<Image*> PhotoData::thumbnailSmall() const { - return _thumbnailSmall.get(); +std::shared_ptr<PhotoMedia> PhotoData::createMediaView() { + if (auto result = activeMediaView()) { + return result; + } + auto result = std::make_shared<PhotoMedia>(this); + _media = result; + return result; } -not_null<Image*> PhotoData::thumbnail() const { - return _thumbnail.get(); -} - -void PhotoData::load(Data::FileOrigin origin) { - _large->load(origin); -} - -not_null<Image*> PhotoData::large() const { - return _large.get(); +std::shared_ptr<PhotoMedia> PhotoData::activeMediaView() const { + return _media.lock(); } void PhotoData::updateImages( - ImagePtr thumbnailInline, - ImagePtr thumbnailSmall, - ImagePtr thumbnail, - ImagePtr large) { - if (!thumbnailSmall || !thumbnail || !large) { - return; + const QByteArray &inlineThumbnailBytes, + const ImageWithLocation &small, + const ImageWithLocation &thumbnail, + const ImageWithLocation &large) { + if (!inlineThumbnailBytes.isEmpty() + && _inlineThumbnailBytes.isEmpty()) { + _inlineThumbnailBytes = inlineThumbnailBytes; } - if (thumbnailInline && !_thumbnailInline) { - _thumbnailInline = thumbnailInline; - } - const auto update = [](ImagePtr &was, ImagePtr now) { - if (!was) { - was = now; - } else if (was->isDelayedStorageImage()) { - if (const auto location = now->location(); location.valid()) { - was->setDelayedStorageLocation( - Data::FileOrigin(), - location); + const auto update = [&](PhotoSize size, const ImageWithLocation &data) { + auto &image = _images[PhotoSizeIndex(size)]; + if (data.location.valid() + && (!image.location.valid() + || image.location.width() != data.location.width() + || image.location.height() != data.location.height())) { + image.location = data.location; + image.byteSize = data.bytesCount; + if (!data.preloaded.isNull()) { + image.loader = nullptr; + if (const auto media = activeMediaView()) { + media->set(size, data.preloaded); + } + } else if (image.loader) { + const auto origin = base::take(image.loader)->fileOrigin(); + load(size, origin); + } + if (!data.bytes.isEmpty()) { + if (const auto cacheKey = image.location.file().cacheKey()) { + owner().cache().putIfEmpty( + cacheKey, + Storage::Cache::Database::TaggedValue( + base::duplicate(data.bytes), + Data::kImageCacheTag)); + } } } + //if (was->isDelayedStorageImage()) { // #TODO optimize + // if (const auto location = now->location(); location.valid()) { + // was->setDelayedStorageLocation( + // Data::FileOrigin(), + // location); + // } + //} }; - update(_thumbnailSmall, thumbnailSmall); - update(_thumbnail, thumbnail); - update(_large, large); + update(PhotoSize::Small, small); + update(PhotoSize::Thumbnail, thumbnail); + update(PhotoSize::Large, large); } int PhotoData::width() const { - return _large->width(); + return _images[PhotoSizeIndex(PhotoSize::Large)].location.width(); } int PhotoData::height() const { - return _large->height(); + return _images[PhotoSizeIndex(PhotoSize::Large)].location.height(); } PhotoClickHandler::PhotoClickHandler( @@ -255,7 +390,7 @@ void PhotoSaveClickHandler::onClickImpl() const { if (!data->date) { return; } else { - data->download(context()); + data->load(context()); } } diff --git a/Telegram/SourceFiles/data/data_photo.h b/Telegram/SourceFiles/data/data_photo.h index 772d1ecb4..f08c8fe57 100644 --- a/Telegram/SourceFiles/data/data_photo.h +++ b/Telegram/SourceFiles/data/data_photo.h @@ -14,8 +14,26 @@ class Session; } // namespace Main namespace Data { + class Session; class ReplyPreview; +class PhotoMedia; + +inline constexpr auto kPhotoSizeCount = 3; + +enum class PhotoSize : uchar { + Small, + Thumbnail, + Large, +}; + +[[nodiscard]] inline int PhotoSizeIndex(PhotoSize size) { + Expects(static_cast<int>(size) >= 0 + && static_cast<int>(size) < kPhotoSizeCount); + + return static_cast<int>(size); +} + } // namespace Data class PhotoData final { @@ -25,20 +43,17 @@ public: [[nodiscard]] Data::Session &owner() const; [[nodiscard]] Main::Session &session() const; + [[nodiscard]] bool isNull() const; - void automaticLoad( - Data::FileOrigin origin, - const HistoryItem *item); void automaticLoadSettingsChanged(); - void download(Data::FileOrigin origin); - [[nodiscard]] bool loaded() const; [[nodiscard]] bool loading() const; [[nodiscard]] bool displayLoading() const; void cancel(); [[nodiscard]] float64 progress() const; [[nodiscard]] int32 loadOffset() const; [[nodiscard]] bool uploading() const; + [[nodiscard]] bool cancelled() const; void setWaitingForAlbum(); [[nodiscard]] bool waitingForAlbum() const; @@ -60,27 +75,39 @@ public: // to (this) received from the server "same" photo. void collectLocalData(not_null<PhotoData*> local); - bool isNull() const; + [[nodiscard]] std::shared_ptr<Data::PhotoMedia> createMediaView(); + [[nodiscard]] auto activeMediaView() const + -> std::shared_ptr<Data::PhotoMedia>; - void loadThumbnail(Data::FileOrigin origin); - void loadThumbnailSmall(Data::FileOrigin origin); - Image *thumbnailInline() const; - not_null<Image*> thumbnailSmall() const; - not_null<Image*> thumbnail() const; + void updateImages( + const QByteArray &inlineThumbnailBytes, + const ImageWithLocation &small, + const ImageWithLocation &thumbnail, + const ImageWithLocation &large); - void load(Data::FileOrigin origin); - not_null<Image*> large() const; + [[nodiscard]] QByteArray inlineThumbnailBytes() const { + return _inlineThumbnailBytes; + } + + void load( + Data::FileOrigin origin, + LoadFromCloudSetting fromCloud = LoadFromCloudOrLocal, + bool autoLoading = false); + + [[nodiscard]] bool loading(Data::PhotoSize size) const; + [[nodiscard]] bool failed(Data::PhotoSize size) const; + void load( + Data::PhotoSize size, + Data::FileOrigin origin, + LoadFromCloudSetting fromCloud = LoadFromCloudOrLocal, + bool autoLoading = false); + [[nodiscard]] const ImageLocation &location(Data::PhotoSize size) const; + [[nodiscard]] int imageByteSize(Data::PhotoSize size) const; // For now they return size of the 'large' image. int width() const; int height() const; - void updateImages( - ImagePtr thumbnailInline, - ImagePtr thumbnailSmall, - ImagePtr thumbnail, - ImagePtr large); - PhotoId id = 0; TimeId date = 0; bool hasSticker = false; @@ -91,15 +118,30 @@ public: std::unique_ptr<Data::UploadState> uploadingData; private: - ImagePtr _thumbnailInline; - ImagePtr _thumbnailSmall; - ImagePtr _thumbnail; - ImagePtr _large; + enum class ImageFlag : uchar { + Cancelled = 0x01, + Failed = 0x02, + }; + friend inline constexpr bool is_flag_type(ImageFlag) { return true; }; + + struct Image final { + ImageLocation location; + std::unique_ptr<FileLoader> loader; + int byteSize = 0; + base::flags<ImageFlag> flags; + }; + + void finishLoad(Data::PhotoSize size); + void destroyLoader(Data::PhotoSize size); + + QByteArray _inlineThumbnailBytes; + std::array<Image, Data::kPhotoSizeCount> _images; int32 _dc = 0; uint64 _access = 0; QByteArray _fileReference; std::unique_ptr<Data::ReplyPreview> _replyPreview; + std::weak_ptr<Data::PhotoMedia> _media; not_null<Data::Session*> _owner; diff --git a/Telegram/SourceFiles/data/data_photo_media.cpp b/Telegram/SourceFiles/data/data_photo_media.cpp new file mode 100644 index 000000000..7f82a124b --- /dev/null +++ b/Telegram/SourceFiles/data/data_photo_media.cpp @@ -0,0 +1,119 @@ +/* +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 "data/data_photo_media.h" + +#include "data/data_photo.h" +#include "data/data_session.h" +#include "data/data_file_origin.h" +#include "data/data_auto_download.h" +#include "main/main_session.h" +#include "history/history_item.h" +#include "history/history.h" +#include "storage/file_download.h" +#include "ui/image/image.h" +//#include "facades.h" +//#include "app.h" + +namespace Data { + +PhotoMedia::PhotoMedia(not_null<PhotoData*> owner) +: _owner(owner) { +} + +// NB! Right now DocumentMedia can outlive Main::Session! +// In DocumentData::collectLocalData a shared_ptr is sent on_main. +// In case this is a problem the ~Gif code should be rewritten. +PhotoMedia::~PhotoMedia() = default; + +not_null<PhotoData*> PhotoMedia::owner() const { + return _owner; +} + +Image *PhotoMedia::thumbnailInline() const { + if (!_inlineThumbnail) { + auto image = Images::FromInlineBytes(_owner->inlineThumbnailBytes()); + if (!image.isNull()) { + _inlineThumbnail = std::make_unique<Image>( + std::make_unique<Images::ImageSource>( + std::move(image), + "PNG")); + } + } + return _inlineThumbnail.get(); +} + +Image *PhotoMedia::image(PhotoSize size) const { + return _images[PhotoSizeIndex(size)].get(); +} + +void PhotoMedia::wanted(PhotoSize size, Data::FileOrigin origin) { + const auto index = PhotoSizeIndex(size); + if (!_images[index]) { + _owner->load(size, origin); + } +} + +QSize PhotoMedia::size(PhotoSize size) const { + const auto index = PhotoSizeIndex(size); + if (const auto image = _images[index].get()) { + return image->size(); + } + const auto &location = _owner->location(size); + return { location.width(), location.height() }; +} + +void PhotoMedia::set(PhotoSize size, QImage image) { + _images[PhotoSizeIndex(size)] = std::make_unique<Image>( + std::make_unique<Images::ImageSource>(std::move(image), "PNG")); + _owner->session().downloaderTaskFinished().notify(); +} + +bool PhotoMedia::loaded() const { + const auto index = PhotoSizeIndex(PhotoSize::Large); + return (_images[index] != nullptr); +} + +float64 PhotoMedia::progress() const { + return (_owner->uploading() || _owner->loading()) + ? _owner->progress() + : (loaded() ? 1. : 0.); +} + +void PhotoMedia::automaticLoad( + Data::FileOrigin origin, + const HistoryItem *item) { + const auto index = PhotoSizeIndex(PhotoSize::Large); + if (!item || loaded() || _owner->cancelled()) { + return; + } + const auto loadFromCloud = Data::AutoDownload::Should( + _owner->session().settings().autoDownload(), + item->history()->peer, + _owner); + _owner->load( + origin, + loadFromCloud ? LoadFromCloudOrLocal : LoadFromLocalOnly, + true); +} + +void PhotoMedia::collectLocalData(not_null<PhotoMedia*> local) { + if (const auto image = local->_inlineThumbnail.get()) { + _inlineThumbnail = std::make_unique<Image>( + std::make_unique<Images::ImageSource>(image->original(), "PNG")); + } + for (auto i = 0; i != kPhotoSizeCount; ++i) { + if (const auto image = local->_images[i].get()) { + _images[i] = std::make_unique<Image>( + std::make_unique<Images::ImageSource>( + image->original(), + "PNG")); + } + } +} + +} // namespace Data diff --git a/Telegram/SourceFiles/data/data_photo_media.h b/Telegram/SourceFiles/data/data_photo_media.h new file mode 100644 index 000000000..09d2f9c2e --- /dev/null +++ b/Telegram/SourceFiles/data/data_photo_media.h @@ -0,0 +1,48 @@ +/* +This file is part of Telegram Desktop, +the official desktop application for the Telegram messaging service. + +For license and copyright information please follow this link: +https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL +*/ +#pragma once + +#include "base/flags.h" +#include "data/data_photo.h" + +class FileLoader; + +namespace Data { + +class PhotoMedia final { +public: + explicit PhotoMedia(not_null<PhotoData*> owner); + ~PhotoMedia(); + + [[nodiscard]] not_null<PhotoData*> owner() const; + + [[nodiscard]] Image *thumbnailInline() const; + + [[nodiscard]] Image *image(PhotoSize size) const; + [[nodiscard]] QSize size(PhotoSize size) const; + void wanted(PhotoSize size, Data::FileOrigin origin); + void set(PhotoSize size, QImage image); + + [[nodiscard]] bool loaded() const; + [[nodiscard]] float64 progress() const; + + void automaticLoad(Data::FileOrigin origin, const HistoryItem *item); + + void collectLocalData(not_null<PhotoMedia*> local); + +private: + // NB! Right now DocumentMedia can outlive Main::Session! + // In DocumentData::collectLocalData a shared_ptr is sent on_main. + // In case this is a problem the ~Gif code should be rewritten. + const not_null<PhotoData*> _owner; + mutable std::unique_ptr<Image> _inlineThumbnail; + std::array<std::unique_ptr<Image>, kPhotoSizeCount> _images; + +}; + +} // namespace Data diff --git a/Telegram/SourceFiles/data/data_reply_preview.cpp b/Telegram/SourceFiles/data/data_reply_preview.cpp index a46e10bea..ba2d84d0b 100644 --- a/Telegram/SourceFiles/data/data_reply_preview.cpp +++ b/Telegram/SourceFiles/data/data_reply_preview.cpp @@ -11,6 +11,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "data/data_document.h" #include "data/data_document_media.h" #include "data/data_photo.h" +#include "data/data_photo_media.h" #include "ui/image/image.h" #include "ui/image/image_source.h" @@ -62,18 +63,18 @@ Image *ReplyPreview::image(Data::FileOrigin origin) { return _image.get(); } if (_document) { - if (!_documentMedia) { - _documentMedia = _document->createMediaView(); - } - const auto thumbnail = _documentMedia->thumbnail(); - if (!_image || (!_good && thumbnail)) { + if (!_image || (!_good && _document->hasThumbnail())) { + if (!_documentMedia) { + _documentMedia = _document->createMediaView(); + _documentMedia->thumbnailWanted(origin); + } + const auto thumbnail = _documentMedia->thumbnail(); const auto option = _document->isVideoMessage() ? Images::Option::Circled : Images::Option::None; if (thumbnail) { prepare(thumbnail, option); } else { - _documentMedia->thumbnailWanted(origin); if (const auto image = _documentMedia->thumbnailInline()) { prepare(image, option | Images::Option::Blurred); } @@ -85,22 +86,33 @@ Image *ReplyPreview::image(Data::FileOrigin origin) { } } else { Assert(_photo != nullptr); - const auto small = _photo->thumbnailSmall(); - const auto large = _photo->large(); - if (!_image || (!_good && (small->loaded() || large->loaded()))) { - if (small->isDelayedStorageImage() - && !large->isNull() - && !large->isDelayedStorageImage() - && large->loaded()) { - prepare(large, Images::Option(0)); - } else if (small->loaded()) { - prepare(small, Images::Option(0)); - } else { - small->load(origin); - if (const auto blurred = _photo->thumbnailInline()) { - prepare(blurred, Images::Option::Blurred); - } + if (!_image || !_good) { + if (!_photoMedia) { + _photoMedia = _photo->createMediaView(); + _photoMedia->wanted(PhotoSize::Small, origin); } + if (const auto small = _photoMedia->image(PhotoSize::Small)) { + prepare(small, Images::Option(0)); + } else if (const auto large = _photoMedia->image( + PhotoSize::Large)) { + prepare(large, Images::Option(0)); + } else if (const auto blurred = _photoMedia->thumbnailInline()) { + prepare(blurred, Images::Option::Blurred); + } + + //if (small->isDelayedStorageImage() // #TODO optimize + // && !large->isNull() + // && !large->isDelayedStorageImage() + // && large->loaded()) { + // prepare(large, Images::Option(0)); + //} else if (small->loaded()) { + // prepare(small, Images::Option(0)); + //} else { + // small->load(origin); + // if (const auto blurred = _photo->thumbnailInline()) { + // prepare(blurred, Images::Option::Blurred); + // } + //} } } return _image.get(); diff --git a/Telegram/SourceFiles/data/data_reply_preview.h b/Telegram/SourceFiles/data/data_reply_preview.h index b607c584d..4e9f0cd74 100644 --- a/Telegram/SourceFiles/data/data_reply_preview.h +++ b/Telegram/SourceFiles/data/data_reply_preview.h @@ -12,6 +12,7 @@ class PhotoData; namespace Data { +class PhotoMedia; class DocumentMedia; struct FileOrigin; @@ -26,11 +27,12 @@ private: void prepare(not_null<Image*> image, Images::Options options); std::unique_ptr<Image> _image; + PhotoData *_photo = nullptr; + DocumentData *_document = nullptr; + std::shared_ptr<PhotoMedia> _photoMedia; + std::shared_ptr<DocumentMedia> _documentMedia; bool _good = false; bool _checked = false; - DocumentData *_document = nullptr; - PhotoData *_photo = nullptr; - std::shared_ptr<DocumentMedia> _documentMedia; }; diff --git a/Telegram/SourceFiles/data/data_session.cpp b/Telegram/SourceFiles/data/data_session.cpp index d29fe9b5c..5fbb81a1f 100644 --- a/Telegram/SourceFiles/data/data_session.cpp +++ b/Telegram/SourceFiles/data/data_session.cpp @@ -75,10 +75,10 @@ using ViewElement = HistoryView::Element; // b: crop 320x320 // c: crop 640x640 // d: crop 1280x1280 -const auto InlineLevels = QByteArray::fromRawData("i", 1); -const auto SmallLevels = QByteArray::fromRawData("sambcxydwi", 10); -const auto ThumbnailLevels = QByteArray::fromRawData("mbcxasydwi", 10); -const auto LargeLevels = QByteArray::fromRawData("yxwmsdcbai", 10); +const auto InlineLevels = "i"_q; +const auto SmallLevels = "sambcxydwi"_q; +const auto ThumbnailLevels = "mbcxasydwi"_q; +const auto LargeLevels = "yxwmsdcbai"_q; void CheckForSwitchInlineButton(not_null<HistoryItem*> item) { if (item->out() || !item->hasSwitchInlineButton()) { @@ -127,23 +127,22 @@ std::vector<UnavailableReason> ExtractUnavailableReasons( }) | ranges::to_vector; } -QByteArray FindDocumentInlineThumbnail(const MTPDdocument &data) { - const auto thumbs = data.vthumbs(); - if (!thumbs) { - return QByteArray(); - } - const auto &list = thumbs->v; +[[nodiscard]] QByteArray FindInlineThumbnail( + const QVector<MTPPhotoSize> &sizes) { const auto i = ranges::find( - list, + sizes, mtpc_photoStrippedSize, &MTPPhotoSize::type); - if (i == list.end()) { - return QByteArray(); - } - return i->c_photoStrippedSize().vbytes().v; + return (i != sizes.end()) + ? i->c_photoStrippedSize().vbytes().v + : QByteArray(); } -MTPPhotoSize FindDocumentThumbnail(const MTPDdocument &data) { +[[nodiscard]] QByteArray FindDocumentInlineThumbnail(const MTPDdocument &data) { + return FindInlineThumbnail(data.vthumbs().value_or_empty()); +} + +[[nodiscard]] MTPPhotoSize FindDocumentThumbnail(const MTPDdocument &data) { const auto area = [](const MTPPhotoSize &size) { static constexpr auto kInvalid = 0; return size.match([](const MTPDphotoSizeEmpty &) { @@ -165,7 +164,7 @@ MTPPhotoSize FindDocumentThumbnail(const MTPDdocument &data) { : MTPPhotoSize(MTP_photoSizeEmpty(MTP_string())); } -std::optional<MTPVideoSize> FindDocumentVideoThumbnail( +[[nodiscard]] std::optional<MTPVideoSize> FindDocumentVideoThumbnail( const MTPDdocument &data) { const auto area = [](const MTPVideoSize &size) { static constexpr auto kInvalid = 0; @@ -184,7 +183,11 @@ std::optional<MTPVideoSize> FindDocumentVideoThumbnail( : std::nullopt; } -rpl::producer<int> PinnedDialogsCountMaxValue( +[[nodiscard]] QByteArray FindPhotoInlineThumbnail(const MTPDphoto &data) { + return FindInlineThumbnail(data.vsizes().v); +} + +[[nodiscard]] rpl::producer<int> PinnedDialogsCountMaxValue( not_null<Main::Session*> session) { return rpl::single( rpl::empty_value() @@ -1099,6 +1102,15 @@ void Session::notifyPhotoLayoutChanged(not_null<const PhotoData*> photo) { } } +void Session::requestPhotoViewRepaint(not_null<const PhotoData*> photo) { + const auto i = _photoItems.find(photo); + if (i != end(_photoItems)) { + for (const auto item : i->second) { + requestItemRepaint(item); + } + } +} + void Session::notifyDocumentLayoutChanged( not_null<const DocumentData*> document) { const auto i = _documentItems.find(document); @@ -1153,6 +1165,20 @@ void Session::documentLoadFail( notifyDocumentLayoutChanged(document); } +void Session::photoLoadProgress(not_null<PhotoData*> photo) { + requestPhotoViewRepaint(photo); +} + +void Session::photoLoadDone(not_null<PhotoData*> photo) { + notifyPhotoLayoutChanged(photo); +} + +void Session::photoLoadFail( + not_null<PhotoData*> photo, + bool started) { + notifyPhotoLayoutChanged(photo); +} + void Session::markMediaRead(not_null<const DocumentData*> document) { const auto i = _documentItems.find(document); if (i != end(_documentItems)) { @@ -2174,11 +2200,10 @@ not_null<PhotoData*> Session::processPhoto( const auto image = [&](const QByteArray &levels) { const auto i = find(levels); return (i == thumbs.end()) - ? ImagePtr() - : Images::Create(base::duplicate(i->second), "JPG"); + ? ImageWithLocation() + : Images::FromImageInMemory(i->second, "JPG"); }; - const auto thumbnailInline = image(InlineLevels); - const auto thumbnailSmall = image(SmallLevels); + const auto small = image(SmallLevels); const auto thumbnail = image(ThumbnailLevels); const auto large = image(LargeLevels); return data.match([&](const MTPDphoto &data) { @@ -2189,8 +2214,8 @@ not_null<PhotoData*> Session::processPhoto( data.vdate().v, data.vdc_id().v, data.is_has_stickers(), - thumbnailInline, - thumbnailSmall, + QByteArray(), + small, thumbnail, large); }, [&](const MTPDphotoEmpty &data) { @@ -2205,10 +2230,10 @@ not_null<PhotoData*> Session::photo( TimeId date, int32 dc, bool hasSticker, - const ImagePtr &thumbnailInline, - const ImagePtr &thumbnailSmall, - const ImagePtr &thumbnail, - const ImagePtr &large) { + const QByteArray &inlineThumbnailBytes, + const ImageWithLocation &small, + const ImageWithLocation &thumbnail, + const ImageWithLocation &large) { const auto result = photo(id); photoApplyFields( result, @@ -2217,8 +2242,8 @@ not_null<PhotoData*> Session::photo( date, dc, hasSticker, - thumbnailInline, - thumbnailSmall, + inlineThumbnailBytes, + small, thumbnail, large); return result; @@ -2252,26 +2277,28 @@ void Session::photoConvert( PhotoData *Session::photoFromWeb( const MTPWebDocument &data, - ImagePtr thumbnail, + const ImageLocation &thumbnailLocation, bool willBecomeNormal) { - const auto large = Images::Create(data); + const auto large = Images::FromWebDocument(data); const auto thumbnailInline = ImagePtr(); - if (large->isNull()) { + if (!large.valid()) { return nullptr; } - auto thumbnailSmall = large; + auto small = large; + auto thumbnail = thumbnailLocation; if (willBecomeNormal) { - const auto width = large->width(); - const auto height = large->height(); + const auto width = large.width(); + const auto height = large.height(); - auto thumbsize = shrinkToKeepAspect(width, height, 100, 100); - thumbnailSmall = Images::Create(thumbsize.width(), thumbsize.height()); + // #TODO optimize + //auto thumbsize = shrinkToKeepAspect(width, height, 100, 100); + //small = Images::Create(thumbsize.width(), thumbsize.height()); - if (thumbnail->isNull()) { - auto mediumsize = shrinkToKeepAspect(width, height, 320, 320); - thumbnail = Images::Create(mediumsize.width(), mediumsize.height()); - } - } else if (thumbnail->isNull()) { + //if (!thumbnail.valid()) { + // auto mediumsize = shrinkToKeepAspect(width, height, 320, 320); + // thumbnail = Images::Create(mediumsize.width(), mediumsize.height()); + //} + } else if (!thumbnail.valid()) { thumbnail = large; } @@ -2282,10 +2309,10 @@ PhotoData *Session::photoFromWeb( base::unixtime::now(), 0, false, - thumbnailInline, - thumbnailSmall, - thumbnail, - large); + QByteArray(), + ImageWithLocation{ .location = small }, + ImageWithLocation{ .location = thumbnail }, + ImageWithLocation{ .location = large }); } void Session::photoApplyFields( @@ -2319,13 +2346,12 @@ void Session::photoApplyFields( }; const auto image = [&](const QByteArray &levels) { const auto i = find(levels); - return (i == sizes.end()) ? ImagePtr() : Images::Create(data, *i); + return (i == sizes.end()) + ? ImageWithLocation() + : Images::FromPhotoSize(_session, data, *i); }; - const auto thumbnailInline = image(InlineLevels); - const auto thumbnailSmall = image(SmallLevels); - const auto thumbnail = image(ThumbnailLevels); const auto large = image(LargeLevels); - if (thumbnailSmall && thumbnail && large) { + if (large.location.valid()) { photoApplyFields( photo, data.vaccess_hash().v, @@ -2333,9 +2359,9 @@ void Session::photoApplyFields( data.vdate().v, data.vdc_id().v, data.is_has_stickers(), - thumbnailInline, - thumbnailSmall, - thumbnail, + FindPhotoInlineThumbnail(data), + image(SmallLevels), + image(ThumbnailLevels), large); } } @@ -2347,10 +2373,10 @@ void Session::photoApplyFields( TimeId date, int32 dc, bool hasSticker, - const ImagePtr &thumbnailInline, - const ImagePtr &thumbnailSmall, - const ImagePtr &thumbnail, - const ImagePtr &large) { + const QByteArray &inlineThumbnailBytes, + const ImageWithLocation &small, + const ImageWithLocation &thumbnail, + const ImageWithLocation &large) { if (!date) { return; } @@ -2358,8 +2384,8 @@ void Session::photoApplyFields( photo->date = date; photo->hasSticker = hasSticker; photo->updateImages( - thumbnailInline, - thumbnailSmall, + inlineThumbnailBytes, + small, thumbnail, large); } diff --git a/Telegram/SourceFiles/data/data_session.h b/Telegram/SourceFiles/data/data_session.h index aea8a2f6c..8ebbbe2c6 100644 --- a/Telegram/SourceFiles/data/data_session.h +++ b/Telegram/SourceFiles/data/data_session.h @@ -427,12 +427,17 @@ public: void documentLoadSettingsChanged(); void notifyPhotoLayoutChanged(not_null<const PhotoData*> photo); + void requestPhotoViewRepaint(not_null<const PhotoData*> photo); void notifyDocumentLayoutChanged( not_null<const DocumentData*> document); void requestDocumentViewRepaint(not_null<const DocumentData*> document); void markMediaRead(not_null<const DocumentData*> document); void requestPollViewRepaint(not_null<const PollData*> poll); + void photoLoadProgress(not_null<PhotoData*> photo); + void photoLoadDone(not_null<PhotoData*> photo); + void photoLoadFail(not_null<PhotoData*> photo, bool started); + void documentLoadProgress(not_null<DocumentData*> document); void documentLoadDone(not_null<DocumentData*> document); void documentLoadFail(not_null<DocumentData*> document, bool started); @@ -473,16 +478,16 @@ public: TimeId date, int32 dc, bool hasSticker, - const ImagePtr &thumbnailInline, - const ImagePtr &thumbnailSmall, - const ImagePtr &thumbnail, - const ImagePtr &large); + const QByteArray &inlineThumbnailBytes, + const ImageWithLocation &small, + const ImageWithLocation &thumbnail, + const ImageWithLocation &large); void photoConvert( not_null<PhotoData*> original, const MTPPhoto &data); [[nodiscard]] PhotoData *photoFromWeb( const MTPWebDocument &data, - ImagePtr thumbnail = ImagePtr(), + const ImageLocation &thumbnailLocation = ImageLocation(), bool willBecomeNormal = false); [[nodiscard]] not_null<DocumentData*> document(DocumentId id); @@ -736,10 +741,10 @@ private: TimeId date, int32 dc, bool hasSticker, - const ImagePtr &thumbnailInline, - const ImagePtr &thumbnailSmall, - const ImagePtr &thumbnail, - const ImagePtr &large); + const QByteArray &inlineThumbnailBytes, + const ImageWithLocation &small, + const ImageWithLocation &thumbnail, + const ImageWithLocation &large); void documentApplyFields( not_null<DocumentData*> document, diff --git a/Telegram/SourceFiles/history/admin_log/history_admin_log_inner.cpp b/Telegram/SourceFiles/history/admin_log/history_admin_log_inner.cpp index 1aad5b722..fdcbaff9e 100644 --- a/Telegram/SourceFiles/history/admin_log/history_admin_log_inner.cpp +++ b/Telegram/SourceFiles/history/admin_log/history_admin_log_inner.cpp @@ -39,6 +39,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "boxes/peers/edit_participants_box.h" #include "data/data_session.h" #include "data/data_photo.h" +#include "data/data_photo_media.h" #include "data/data_document.h" #include "data/data_media_types.h" #include "data/data_file_origin.h" @@ -1137,11 +1138,13 @@ void InnerWidget::showContextMenu(QContextMenuEvent *e, bool showFromTouch) { } } -void InnerWidget::savePhotoToFile(PhotoData *photo) { - if (!photo || photo->isNull() || !photo->loaded()) { +void InnerWidget::savePhotoToFile(not_null<PhotoData*> photo) { + const auto media = photo->activeMediaView(); + if (photo->isNull() || !media || !media->loaded()) { return; } + const auto image = media->image(Data::PhotoSize::Large)->original(); auto filter = qsl("JPEG Image (*.jpg);;") + FileDialog::AllFilesFilter(); FileDialog::GetWritePath( this, @@ -1150,22 +1153,26 @@ void InnerWidget::savePhotoToFile(PhotoData *photo) { filedialogDefaultName(qsl("photo"), qsl(".jpg")), crl::guard(this, [=](const QString &result) { if (!result.isEmpty()) { - photo->large()->original().save(result, "JPG"); + image.save(result, "JPG"); } })); } -void InnerWidget::saveDocumentToFile(DocumentData *document) { +void InnerWidget::saveDocumentToFile(not_null<DocumentData*> document) { DocumentSaveClickHandler::Save( Data::FileOrigin(), document, DocumentSaveClickHandler::Mode::ToNewFile); } -void InnerWidget::copyContextImage(PhotoData *photo) { - if (!photo || photo->isNull() || !photo->loaded()) return; +void InnerWidget::copyContextImage(not_null<PhotoData*> photo) { + const auto media = photo->activeMediaView(); + if (photo->isNull() || !media || !media->loaded()) { + return; + } - QGuiApplication::clipboard()->setImage(photo->large()->original()); + const auto image = media->image(Data::PhotoSize::Large)->original(); + QGuiApplication::clipboard()->setImage(image); } void InnerWidget::copySelectedText() { diff --git a/Telegram/SourceFiles/history/admin_log/history_admin_log_inner.h b/Telegram/SourceFiles/history/admin_log/history_admin_log_inner.h index b539ed42f..dd00546d5 100644 --- a/Telegram/SourceFiles/history/admin_log/history_admin_log_inner.h +++ b/Telegram/SourceFiles/history/admin_log/history_admin_log_inner.h @@ -162,9 +162,9 @@ private: QPoint mapPointToItem(QPoint point, const Element *view) const; void showContextMenu(QContextMenuEvent *e, bool showFromTouch = false); - void savePhotoToFile(PhotoData *photo); - void saveDocumentToFile(DocumentData *document); - void copyContextImage(PhotoData *photo); + void savePhotoToFile(not_null<PhotoData*> photo); + void saveDocumentToFile(not_null<DocumentData*> document); + void copyContextImage(not_null<PhotoData*> photo); void showStickerPackInfo(not_null<DocumentData*> document); void cancelContextDownload(not_null<DocumentData*> document); void showContextInFolder(not_null<DocumentData*> document); diff --git a/Telegram/SourceFiles/history/history_inner_widget.cpp b/Telegram/SourceFiles/history/history_inner_widget.cpp index c372d017e..ead566496 100644 --- a/Telegram/SourceFiles/history/history_inner_widget.cpp +++ b/Telegram/SourceFiles/history/history_inner_widget.cpp @@ -51,6 +51,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "data/data_document.h" #include "data/data_poll.h" #include "data/data_photo.h" +#include "data/data_photo_media.h" #include "data/data_user.h" #include "data/data_file_origin.h" #include "data/data_histories.h" @@ -1853,8 +1854,12 @@ void HistoryInner::copySelectedText() { } void HistoryInner::savePhotoToFile(not_null<PhotoData*> photo) { - if (photo->isNull() || !photo->loaded()) return; + const auto media = photo->activeMediaView(); + if (photo->isNull() || !media || !media->loaded()) { + return; + } + const auto image = media->image(Data::PhotoSize::Large)->original(); auto filter = qsl("JPEG Image (*.jpg);;") + FileDialog::AllFilesFilter(); FileDialog::GetWritePath( this, @@ -1865,15 +1870,19 @@ void HistoryInner::savePhotoToFile(not_null<PhotoData*> photo) { qsl(".jpg")), crl::guard(this, [=](const QString &result) { if (!result.isEmpty()) { - photo->large()->original().save(result, "JPG"); + image.save(result, "JPG"); } })); } void HistoryInner::copyContextImage(not_null<PhotoData*> photo) { - if (photo->isNull() || !photo->loaded()) return; + const auto media = photo->activeMediaView(); + if (photo->isNull() || !media || !media->loaded()) { + return; + } - QGuiApplication::clipboard()->setImage(photo->large()->original()); + const auto image = media->image(Data::PhotoSize::Large)->original(); + QGuiApplication::clipboard()->setImage(image); } void HistoryInner::showStickerPackInfo(not_null<DocumentData*> document) { diff --git a/Telegram/SourceFiles/history/view/history_view_context_menu.cpp b/Telegram/SourceFiles/history/view/history_view_context_menu.cpp index 95c549740..1f855f440 100644 --- a/Telegram/SourceFiles/history/view/history_view_context_menu.cpp +++ b/Telegram/SourceFiles/history/view/history_view_context_menu.cpp @@ -24,6 +24,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "boxes/confirm_box.h" #include "boxes/sticker_set_box.h" #include "data/data_photo.h" +#include "data/data_photo_media.h" #include "data/data_document.h" #include "data/data_media_types.h" #include "data/data_session.h" @@ -72,10 +73,12 @@ MsgId ItemIdAcrossData(not_null<HistoryItem*> item) { } void SavePhotoToFile(not_null<PhotoData*> photo) { - if (photo->isNull() || !photo->loaded()) { + const auto media = photo->activeMediaView(); + if (photo->isNull() || !media || !media->loaded()) { return; } + const auto image = media->image(Data::PhotoSize::Large)->original(); FileDialog::GetWritePath( Core::App().getFileDialogParent(), tr::lng_save_photo(tr::now), @@ -83,17 +86,19 @@ void SavePhotoToFile(not_null<PhotoData*> photo) { filedialogDefaultName(qsl("photo"), qsl(".jpg")), crl::guard(&photo->session(), [=](const QString &result) { if (!result.isEmpty()) { - photo->large()->original().save(result, "JPG"); + image.save(result, "JPG"); } })); } void CopyImage(not_null<PhotoData*> photo) { - if (photo->isNull() || !photo->loaded()) { + const auto media = photo->activeMediaView(); + if (photo->isNull() || !media || !media->loaded()) { return; } - QGuiApplication::clipboard()->setImage(photo->large()->original()); + const auto image = media->image(Data::PhotoSize::Large)->original(); + QGuiApplication::clipboard()->setImage(image); } void ShowStickerPackInfo(not_null<DocumentData*> document) { diff --git a/Telegram/SourceFiles/history/view/media/history_view_photo.cpp b/Telegram/SourceFiles/history/view/media/history_view_photo.cpp index 2d5cb756e..ad63d357d 100644 --- a/Telegram/SourceFiles/history/view/media/history_view_photo.cpp +++ b/Telegram/SourceFiles/history/view/media/history_view_photo.cpp @@ -18,11 +18,17 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "ui/grouped_layout.h" #include "data/data_session.h" #include "data/data_photo.h" +#include "data/data_photo_media.h" #include "data/data_file_origin.h" #include "app.h" #include "styles/style_history.h" namespace HistoryView { +namespace { + +using Data::PhotoSize; + +} // namespace Photo::Photo( not_null<Element*> parent, @@ -46,18 +52,48 @@ Photo::Photo( create(parent->data()->fullId(), chat); } +Photo::~Photo() = default; + void Photo::create(FullMsgId contextId, PeerData *chat) { setLinks( std::make_shared<PhotoOpenClickHandler>(_data, contextId, chat), std::make_shared<PhotoSaveClickHandler>(_data, contextId, chat), std::make_shared<PhotoCancelClickHandler>(_data, contextId, chat)); - if (!_data->thumbnailInline() - && !_data->loaded() - && !_data->thumbnail()->loaded()) { - _data->thumbnailSmall()->load(contextId); + if ((_dataMedia = _data->activeMediaView())) { + dataMediaCreated(); + } else if (_data->inlineThumbnailBytes().isEmpty()) { + _data->load(PhotoSize::Small, contextId); } } +void Photo::ensureDataMediaCreated() const { + if (_dataMedia) { + return; + } + _dataMedia = _data->createMediaView(); + dataMediaCreated(); +} + +void Photo::dataMediaCreated() const { + Expects(_dataMedia != nullptr); + + if (!_dataMedia->image(PhotoSize::Large) + && !_dataMedia->image(PhotoSize::Thumbnail)) { + _dataMedia->wanted(PhotoSize::Small, _realParent->fullId()); + } + history()->owner().registerHeavyViewPart(_parent); +} + +void Photo::checkHeavyPart() { + if (!_dataMedia) { + history()->owner().unregisterHeavyViewPart(_parent); + } +} + +void Photo::unloadHeavyPart() { + _dataMedia = nullptr; +} + QSize Photo::countOptimalSize() { if (_parent->media() != this) { _caption = Ui::Text::String(); @@ -145,9 +181,10 @@ QSize Photo::countCurrentSize(int newWidth) { void Photo::draw(Painter &p, const QRect &r, TextSelection selection, crl::time ms) const { if (width() < st::msgPadding.left() + st::msgPadding.right() + 1) return; - _data->automaticLoad(_realParent->fullId(), _parent->data()); + ensureDataMediaCreated(); + _dataMedia->automaticLoad(_realParent->fullId(), _parent->data()); auto selected = (selection == FullSelection); - auto loaded = _data->loaded(); + auto loaded = _dataMedia->loaded(); auto displayLoading = _data->displayLoading(); auto inWebPage = (_parent->media() != this); @@ -159,7 +196,7 @@ void Photo::draw(Painter &p, const QRect &r, TextSelection selection, crl::time if (displayLoading) { ensureAnimation(); if (!_animation->radial.animating()) { - _animation->radial.start(_data->progress()); + _animation->radial.start(_dataMedia->progress()); } } const auto radial = isRadialAnimation(); @@ -167,13 +204,15 @@ void Photo::draw(Painter &p, const QRect &r, TextSelection selection, crl::time auto rthumb = style::rtlrect(paintx, painty, paintw, painth, width()); if (_serviceWidth > 0) { const auto pix = [&] { - if (loaded) { - return _data->large()->pixCircled(_realParent->fullId(), _pixw, _pixh); - } else if (_data->thumbnail()->loaded()) { - return _data->thumbnail()->pixBlurredCircled(_realParent->fullId(), _pixw, _pixh); - } else if (_data->thumbnailSmall()->loaded()) { - return _data->thumbnailSmall()->pixBlurredCircled(_realParent->fullId(), _pixw, _pixh); - } else if (const auto blurred = _data->thumbnailInline()) { + if (const auto large = _dataMedia->image(PhotoSize::Large)) { + return large->pixCircled(_realParent->fullId(), _pixw, _pixh); + } else if (const auto thumbnail = _dataMedia->image( + PhotoSize::Thumbnail)) { + return thumbnail->pixBlurredCircled(_realParent->fullId(), _pixw, _pixh); + } else if (const auto small = _dataMedia->image( + PhotoSize::Small)) { + return small->pixBlurredCircled(_realParent->fullId(), _pixw, _pixh); + } else if (const auto blurred = _dataMedia->thumbnailInline()) { return blurred->pixBlurredCircled(_realParent->fullId(), _pixw, _pixh); } else { return QPixmap(); @@ -197,13 +236,15 @@ void Photo::draw(Painter &p, const QRect &r, TextSelection selection, crl::time auto roundCorners = inWebPage ? RectPart::AllCorners : ((isBubbleTop() ? (RectPart::TopLeft | RectPart::TopRight) : RectPart::None) | ((isBubbleBottom() && _caption.isEmpty()) ? (RectPart::BottomLeft | RectPart::BottomRight) : RectPart::None)); const auto pix = [&] { - if (loaded) { - return _data->large()->pixSingle(_realParent->fullId(), _pixw, _pixh, paintw, painth, roundRadius, roundCorners); - } else if (_data->thumbnail()->loaded()) { - return _data->thumbnail()->pixBlurredSingle(_realParent->fullId(), _pixw, _pixh, paintw, painth, roundRadius, roundCorners); - } else if (_data->thumbnailSmall()->loaded()) { - return _data->thumbnailSmall()->pixBlurredSingle(_realParent->fullId(), _pixw, _pixh, paintw, painth, roundRadius, roundCorners); - } else if (const auto blurred = _data->thumbnailInline()) { + if (const auto large = _dataMedia->image(PhotoSize::Large)) { + return large->pixSingle(_realParent->fullId(), _pixw, _pixh, paintw, painth, roundRadius, roundCorners); + } else if (const auto thumbnail = _dataMedia->image( + PhotoSize::Thumbnail)) { + return thumbnail->pixBlurredSingle(_realParent->fullId(), _pixw, _pixh, paintw, painth, roundRadius, roundCorners); + } else if (const auto small = _dataMedia->image( + PhotoSize::Small)) { + return small->pixBlurredSingle(_realParent->fullId(), _pixw, _pixh, paintw, painth, roundRadius, roundCorners); + } else if (const auto blurred = _dataMedia->thumbnailInline()) { return blurred->pixBlurredSingle(_realParent->fullId(), _pixw, _pixh, paintw, painth, roundRadius, roundCorners); } else { return QPixmap(); @@ -241,7 +282,7 @@ void Photo::draw(Painter &p, const QRect &r, TextSelection selection, crl::time auto icon = [&]() -> const style::icon* { if (radial || _data->loading()) { if (_data->uploading() - || _data->large()->location().valid()) { + || _data->location(PhotoSize::Large).valid()) { return &(selected ? st::historyFileThumbCancelSelected : st::historyFileThumbCancel); } return nullptr; @@ -304,12 +345,13 @@ TextState Photo::textState(QPoint point, StateRequest request) const { painth -= st::mediaCaptionSkip; } if (QRect(paintx, painty, paintw, painth).contains(point)) { + ensureDataMediaCreated(); if (_data->uploading()) { result.link = _cancell; - } else if (_data->loaded()) { + } else if (_dataMedia->loaded()) { result.link = _openl; } else if (_data->loading()) { - if (_data->large()->location().valid()) { + if (_data->location(PhotoSize::Large).valid()) { result.link = _cancell; } } else { @@ -349,19 +391,20 @@ void Photo::drawGrouped( RectParts corners, not_null<uint64*> cacheKey, not_null<QPixmap*> cache) const { - _data->automaticLoad(_realParent->fullId(), _parent->data()); + ensureDataMediaCreated(); + _dataMedia->automaticLoad(_realParent->fullId(), _parent->data()); validateGroupedCache(geometry, corners, cacheKey, cache); const auto selected = (selection == FullSelection); - const auto loaded = _data->loaded(); + const auto loaded = _dataMedia->loaded(); const auto displayLoading = _data->displayLoading(); const auto bubble = _parent->hasBubble(); if (displayLoading) { ensureAnimation(); if (!_animation->radial.animating()) { - _animation->radial.start(_data->progress()); + _animation->radial.start(_dataMedia->progress()); } } const auto radial = isRadialAnimation(); @@ -414,7 +457,7 @@ void Photo::drawGrouped( return &(selected ? st::historyFileThumbWaitingSelected : st::historyFileThumbWaiting); } else if (radial || _data->loading()) { if (_data->uploading() - || _data->large()->location().valid()) { + || _data->location(PhotoSize::Large).valid()) { return &(selected ? st::historyFileThumbCancelSelected : st::historyFileThumbCancel); } return nullptr; @@ -455,19 +498,21 @@ TextState Photo::getStateGrouped( if (!geometry.contains(point)) { return {}; } + ensureDataMediaCreated(); return TextState(_parent, _data->uploading() ? _cancell - : _data->loaded() + : _dataMedia->loaded() ? _openl : _data->loading() - ? (_data->large()->location().valid() + ? (_data->location(PhotoSize::Large).valid() ? _cancell : nullptr) : _savel); } float64 Photo::dataProgress() const { - return _data->progress(); + ensureDataMediaCreated(); + return _dataMedia->progress(); } bool Photo::dataFinished() const { @@ -476,7 +521,8 @@ bool Photo::dataFinished() const { } bool Photo::dataLoaded() const { - return _data->loaded(); + ensureDataMediaCreated(); + return _dataMedia->loaded(); } bool Photo::needInfoDisplay() const { @@ -491,12 +537,15 @@ void Photo::validateGroupedCache( not_null<uint64*> cacheKey, not_null<QPixmap*> cache) const { using Option = Images::Option; - const auto loaded = _data->loaded(); + + ensureDataMediaCreated(); + + const auto loaded = _dataMedia->loaded(); const auto loadLevel = loaded ? 2 - : (_data->thumbnailInline() - || _data->thumbnail()->loaded() - || _data->thumbnailSmall()->loaded()) + : (_dataMedia->thumbnailInline() + || _dataMedia->image(PhotoSize::Small) + || _dataMedia->image(PhotoSize::Thumbnail)) ? 1 : 0; const auto width = geometry.width(); @@ -523,14 +572,14 @@ void Photo::validateGroupedCache( { width, height }); const auto pixWidth = pixSize.width() * cIntRetinaFactor(); const auto pixHeight = pixSize.height() * cIntRetinaFactor(); - const auto image = loaded - ? _data->large().get() - : _data->thumbnail()->loaded() - ? _data->thumbnail().get() - : _data->thumbnailSmall()->loaded() - ? _data->thumbnailSmall().get() - : _data->thumbnailInline() - ? _data->thumbnailInline() + const auto image = _dataMedia->image(PhotoSize::Large) + ? _dataMedia->image(PhotoSize::Large) + : _dataMedia->image(PhotoSize::Thumbnail) + ? _dataMedia->image(PhotoSize::Thumbnail) + : _dataMedia->image(PhotoSize::Small) + ? _dataMedia->image(PhotoSize::Small) + : _dataMedia->thumbnailInline() + ? _dataMedia->thumbnailInline() : Image::BlankMedia().get(); *cacheKey = key; @@ -556,7 +605,8 @@ bool Photo::needsBubble() const { } bool Photo::isReadyForOpen() const { - return _data->loaded(); + ensureDataMediaCreated(); + return _dataMedia->loaded(); } void Photo::parentTextUpdated() { diff --git a/Telegram/SourceFiles/history/view/media/history_view_photo.h b/Telegram/SourceFiles/history/view/media/history_view_photo.h index fd9295b90..4e80d9c4f 100644 --- a/Telegram/SourceFiles/history/view/media/history_view_photo.h +++ b/Telegram/SourceFiles/history/view/media/history_view_photo.h @@ -9,6 +9,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "history/view/media/history_view_file.h" +namespace Data { +class PhotoMedia; +} // namespace Data + namespace HistoryView { class Photo : public File { @@ -22,6 +26,7 @@ public: not_null<PeerData*> chat, not_null<PhotoData*> photo, int width); + ~Photo(); void draw(Painter &p, const QRect &clip, TextSelection selection, crl::time ms) const override; TextState textState(QPoint point, StateRequest request) const override; @@ -75,6 +80,9 @@ public: void parentTextUpdated() override; + void checkHeavyPart() override; + void unloadHeavyPart() override; + protected: float64 dataProgress() const override; bool dataFinished() const override; @@ -83,6 +91,9 @@ protected: private: void create(FullMsgId contextId, PeerData *chat = nullptr); + void ensureDataMediaCreated() const; + void dataMediaCreated() const; + QSize countOptimalSize() override; QSize countCurrentSize(int newWidth) override; @@ -98,6 +109,7 @@ private: int _pixw = 1; int _pixh = 1; Ui::Text::String _caption; + mutable std::shared_ptr<Data::PhotoMedia> _dataMedia; }; diff --git a/Telegram/SourceFiles/history/view/media/history_view_web_page.cpp b/Telegram/SourceFiles/history/view/media/history_view_web_page.cpp index e17034226..04c33aee5 100644 --- a/Telegram/SourceFiles/history/view/media/history_view_web_page.cpp +++ b/Telegram/SourceFiles/history/view/media/history_view_web_page.cpp @@ -22,6 +22,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "data/data_media_types.h" #include "data/data_web_page.h" #include "data/data_photo.h" +#include "data/data_photo_media.h" #include "data/data_file_origin.h" #include "app.h" #include "styles/style_history.h" @@ -32,15 +33,17 @@ namespace { constexpr auto kMaxOriginalEntryLines = 8192; int articleThumbWidth(not_null<PhotoData*> thumb, int height) { - auto w = thumb->thumbnail()->width(); - auto h = thumb->thumbnail()->height(); - return qMax(qMin(height * w / h, height), 1); + const auto size = thumb->location(Data::PhotoSize::Thumbnail); + return size.height() + ? qMax(qMin(height * size.width() / size.height(), height), 1) + : 1; } -int articleThumbHeight(not_null<PhotoData*> thumb, int width) { - return qMax( - thumb->thumbnail()->height() * width / thumb->thumbnail()->width(), - 1); +int articleThumbHeight(not_null<Data::PhotoMedia*> thumb, int width) { + const auto size = thumb->size(Data::PhotoSize::Thumbnail); + return size.width() + ? std::max(size.height() * width / size.width(), 1) + : 1; } std::vector<std::unique_ptr<Data::Media>> PrepareCollageMedia( @@ -410,6 +413,31 @@ void WebPage::refreshParentId(not_null<HistoryItem*> realParent) { } } +void WebPage::ensurePhotoMediaCreated() const { + Expects(_data->photo != nullptr); + + if (_photoMedia) { + return; + } + _photoMedia = _data->photo->createMediaView(); + const auto contextId = _parent->data()->fullId(); + _photoMedia->wanted(Data::PhotoSize::Thumbnail, contextId); + history()->owner().registerHeavyViewPart(_parent); +} + +void WebPage::checkHeavyPart() { + if (_attach) { + _attach->checkHeavyPart(); + } +} + +void WebPage::unloadHeavyPart() { + if (_attach) { + _attach->unloadHeavyPart(); + } + _photoMedia = nullptr; +} + void WebPage::draw(Painter &p, const QRect &r, TextSelection selection, crl::time ms) const { if (width() < st::msgPadding.left() + st::msgPadding.right() + 1) return; auto paintx = 0, painty = 0, paintw = width(), painth = height(); @@ -440,25 +468,28 @@ void WebPage::draw(Painter &p, const QRect &r, TextSelection selection, crl::tim auto lineHeight = unitedLineHeight(); if (asArticle()) { + ensurePhotoMediaCreated(); + const auto contextId = _parent->data()->fullId(); - _data->photo->loadThumbnail(contextId); - bool full = _data->photo->thumbnail()->loaded(); QPixmap pix; auto pw = qMax(_pixw, lineHeight); auto ph = _pixh; - auto pixw = _pixw, pixh = articleThumbHeight(_data->photo, _pixw); - const auto maxw = style::ConvertScale(_data->photo->thumbnail()->width()); - const auto maxh = style::ConvertScale(_data->photo->thumbnail()->height()); + auto pixw = _pixw, pixh = articleThumbHeight(_photoMedia.get(), _pixw); + const auto maxsize = _photoMedia->size(Data::PhotoSize::Thumbnail); + const auto maxw = style::ConvertScale(maxsize.width()); + const auto maxh = style::ConvertScale(maxsize.height()); if (pixw * ph != pixh * pw) { float64 coef = (pixw * ph > pixh * pw) ? qMin(ph / float64(pixh), maxh / float64(pixh)) : qMin(pw / float64(pixw), maxw / float64(pixw)); pixh = qRound(pixh * coef); pixw = qRound(pixw * coef); } - if (full) { - pix = _data->photo->thumbnail()->pixSingle(contextId, pixw, pixh, pw, ph, ImageRoundRadius::Small); - } else if (_data->photo->thumbnailSmall()->loaded()) { - pix = _data->photo->thumbnailSmall()->pixBlurredSingle(contextId, pixw, pixh, pw, ph, ImageRoundRadius::Small); - } else if (const auto blurred = _data->photo->thumbnailInline()) { + if (const auto thumbnail = _photoMedia->image( + Data::PhotoSize::Thumbnail)) { + pix = thumbnail->pixSingle(contextId, pixw, pixh, pw, ph, ImageRoundRadius::Small); + } else if (const auto small = _photoMedia->image( + Data::PhotoSize::Small)) { + pix = small->pixBlurredSingle(contextId, pixw, pixh, pw, ph, ImageRoundRadius::Small); + } else if (const auto blurred = _photoMedia->thumbnailInline()) { pix = blurred->pixBlurredSingle(contextId, pixw, pixh, pw, ph, ImageRoundRadius::Small); } p.drawPixmapLeft(padding.left() + paintw - pw, tshift, width(), pix); diff --git a/Telegram/SourceFiles/history/view/media/history_view_web_page.h b/Telegram/SourceFiles/history/view/media/history_view_web_page.h index 95afb0cbb..c3d9ab429 100644 --- a/Telegram/SourceFiles/history/view/media/history_view_web_page.h +++ b/Telegram/SourceFiles/history/view/media/history_view_web_page.h @@ -11,6 +11,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL namespace Data { class Media; +class PhotoMedia; } // namespace Data namespace HistoryView { @@ -85,16 +86,8 @@ public: return _attach.get(); } - void checkHeavyPart() override { - if (_attach) { - _attach->checkHeavyPart(); - } - } - void unloadHeavyPart() override { - if (_attach) { - _attach->unloadHeavyPart(); - } - } + void checkHeavyPart() override; + void unloadHeavyPart() override; ~WebPage(); @@ -103,6 +96,8 @@ private: QSize countOptimalSize() override; QSize countCurrentSize(int newWidth) override; + void ensurePhotoMediaCreated() const; + TextSelection toTitleSelection(TextSelection selection) const; TextSelection fromTitleSelection(TextSelection selection) const; TextSelection toDescriptionSelection(TextSelection selection) const; @@ -119,6 +114,7 @@ private: std::vector<std::unique_ptr<Data::Media>> _collage; ClickHandlerPtr _openl; std::unique_ptr<Media> _attach; + mutable std::shared_ptr<Data::PhotoMedia> _photoMedia; bool _asArticle = false; int _dataVersion = -1; diff --git a/Telegram/SourceFiles/inline_bots/inline_bot_layout_internal.cpp b/Telegram/SourceFiles/inline_bots/inline_bot_layout_internal.cpp index d74e0f8ea..050876814 100644 --- a/Telegram/SourceFiles/inline_bots/inline_bot_layout_internal.cpp +++ b/Telegram/SourceFiles/inline_bots/inline_bot_layout_internal.cpp @@ -11,6 +11,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "data/data_document.h" #include "data/data_session.h" #include "data/data_file_origin.h" +#include "data/data_photo_media.h" #include "data/data_document_media.h" #include "styles/style_overview.h" #include "styles/style_history.h" @@ -47,7 +48,9 @@ FileBase::FileBase(not_null<Context*> context, not_null<Result*> result) : ItemBase(context, result) { } -FileBase::FileBase(not_null<Context*> context, DocumentData *document) +FileBase::FileBase( + not_null<Context*> context, + not_null<DocumentData*> document) : ItemBase(context, document) { } @@ -87,10 +90,16 @@ int FileBase::content_duration() const { return getResultDuration(); } -Gif::Gif(not_null<Context*> context, Result *result) : FileBase(context, result) { +Gif::Gif(not_null<Context*> context, not_null<Result*> result) +: FileBase(context, result) { + Expects(getResultDocument() != nullptr); } -Gif::Gif(not_null<Context*> context, DocumentData *document, bool hasDeleteButton) : FileBase(context, document) { +Gif::Gif( + not_null<Context*> context, + not_null<DocumentData*> document, + bool hasDeleteButton) +: FileBase(context, document) { if (hasDeleteButton) { _delete = std::make_shared<DeleteSavedGifClickHandler>(document); } @@ -428,7 +437,7 @@ void Gif::clipCallback(Media::Clip::Notification notification) { } } -Sticker::Sticker(not_null<Context*> context, Result *result) +Sticker::Sticker(not_null<Context*> context, not_null<Result*> result) : FileBase(context, result) { } @@ -455,6 +464,12 @@ void Sticker::ensureDataMediaCreated(not_null<DocumentData*> document) const { _dataMedia = document->createMediaView(); } +void Sticker::unloadHeavyPart() { + _dataMedia = nullptr; + _lifetime.destroy(); + _lottie = nullptr; +} + void Sticker::paint(Painter &p, const QRect &clip, const PaintContext *context) const { ensureDataMediaCreated(getShownDocument()); bool loaded = _dataMedia->loaded(); @@ -572,8 +587,9 @@ void Sticker::prepareThumbnail() const { } } -Photo::Photo(not_null<Context*> context, Result *result) +Photo::Photo(not_null<Context*> context, not_null<Result*> result) : ItemBase(context, result) { + Expects(getShownPhoto() != nullptr); } void Photo::initDimensions() { @@ -611,15 +627,20 @@ TextState Photo::getState( return {}; } +void Photo::unloadHeavyPart() { + getShownPhoto()->unload(); + _photoMedia = nullptr; +} + PhotoData *Photo::getShownPhoto() const { - if (PhotoData *result = getPhoto()) { + if (const auto result = getPhoto()) { return result; } return getResultPhoto(); } QSize Photo::countFrameSize() const { - PhotoData *photo = getShownPhoto(); + const auto photo = getShownPhoto(); int32 framew = photo->width(), frameh = photo->height(), height = st::inlineMediaHeight; if (framew * height > frameh * _width) { if (framew < st::maxStickerSize || frameh > height) { @@ -672,15 +693,20 @@ void Photo::validateThumbnail( void Photo::prepareThumbnail(QSize size, QSize frame) const { if (const auto photo = getShownPhoto()) { - validateThumbnail(photo->thumbnail(), size, frame, true); - validateThumbnail(photo->thumbnailSmall(), size, frame, false); - validateThumbnail(photo->thumbnailInline(), size, frame, false); + using PhotoSize = Data::PhotoSize; + if (!_photoMedia) { + _photoMedia = photo->createMediaView(); + _photoMedia->wanted(PhotoSize::Thumbnail, fileOrigin()); + } + validateThumbnail(_photoMedia->image(PhotoSize::Thumbnail), size, frame, true); + validateThumbnail(_photoMedia->image(PhotoSize::Small), size, frame, false); + validateThumbnail(_photoMedia->thumbnailInline(), size, frame, false); } else if (const auto thumbnail = getResultThumb()) { validateThumbnail(thumbnail, size, frame, true); } } -Video::Video(not_null<Context*> context, Result *result) +Video::Video(not_null<Context*> context, not_null<Result*> result) : FileBase(context, result) , _link(getResultPreviewHandler()) , _title(st::emojiPanWidth - st::emojiScroll.width - st::inlineResultsLeft - st::inlineThumbSize - st::inlineThumbSkip) @@ -765,6 +791,10 @@ void Video::paint(Painter &p, const QRect &clip, const PaintContext *context) co } } +void Video::unloadHeavyPart() { + _documentMedia = nullptr; +} + TextState Video::getState( QPoint point, StateRequest request) const { @@ -951,6 +981,10 @@ void File::clickHandlerActiveChanged(const ClickHandlerPtr &p, bool active) { } } +void File::unloadHeavyPart() { + _documentMedia = nullptr; +} + File::~File() { unregDocumentItem(_document, this); } @@ -1056,7 +1090,8 @@ void File::setStatusSize(int32 newSize, int32 fullSize, int32 duration, qint64 r } } -Contact::Contact(not_null<Context*> context, Result *result) : ItemBase(context, result) +Contact::Contact(not_null<Context*> context, not_null<Result*> result) +: ItemBase(context, result) , _title(st::emojiPanWidth - st::emojiScroll.width - st::inlineResultsLeft - st::inlineThumbSize - st::inlineThumbSkip) , _description(st::emojiPanWidth - st::emojiScroll.width - st::inlineResultsLeft - st::inlineThumbSize - st::inlineThumbSkip) { } @@ -1111,7 +1146,7 @@ TextState Contact::getState( } void Contact::prepareThumbnail(int width, int height) const { - const auto thumb = getResultThumb(); + const auto thumb = getResultThumb(); // #TODO optimize if (!thumb) { if (_thumb.width() != width * cIntRetinaFactor() || _thumb.height() != height * cIntRetinaFactor()) { _thumb = getResultContactAvatar(width, height); @@ -1142,7 +1177,11 @@ void Contact::prepareThumbnail(int width, int height) const { } } -Article::Article(not_null<Context*> context, Result *result, bool withThumb) : ItemBase(context, result) +Article::Article( + not_null<Context*> context, + not_null<Result*> result, + bool withThumb) +: ItemBase(context, result) , _url(getResultUrlHandler()) , _link(getResultPreviewHandler()) , _withThumb(withThumb) @@ -1196,7 +1235,7 @@ void Article::paint(Painter &p, const QRect &clip, const PaintContext *context) prepareThumbnail(st::inlineThumbSize, st::inlineThumbSize); QRect rthumb(style::rtlrect(0, st::inlineRowMargin, st::inlineThumbSize, st::inlineThumbSize, _width)); if (_thumb.isNull()) { - const auto thumb = getResultThumb(); + const auto thumb = getResultThumb(); // #TODO optimize if (!thumb && !_thumbLetter.isEmpty()) { int32 index = (_thumbLetter.at(0).unicode() % 4); style::color colors[] = { @@ -1261,7 +1300,7 @@ TextState Article::getState( } void Article::prepareThumbnail(int width, int height) const { - const auto thumb = getResultThumb(); + const auto thumb = getResultThumb(); // #TODO optimize if (!thumb) { if (_thumb.width() != width * cIntRetinaFactor() || _thumb.height() != height * cIntRetinaFactor()) { _thumb = getResultContactAvatar(width, height); @@ -1292,7 +1331,8 @@ void Article::prepareThumbnail(int width, int height) const { } } -Game::Game(not_null<Context*> context, Result *result) : ItemBase(context, result) +Game::Game(not_null<Context*> context, not_null<Result*> result) +: ItemBase(context, result) , _title(st::emojiPanWidth - st::emojiScroll.width - st::inlineResultsLeft - st::inlineThumbSize - st::inlineThumbSkip) , _description(st::emojiPanWidth - st::emojiScroll.width - st::inlineResultsLeft - st::inlineThumbSize - st::inlineThumbSkip) { countFrameSize(); @@ -1359,18 +1399,21 @@ void Game::paint(Painter &p, const QRect &clip, const PaintContext *context) con // Gif thumb auto thumbDisplayed = false, radial = false; + const auto photo = getResultPhoto(); const auto document = getResultDocument(); if (document) { ensureDataMediaCreated(document); + } else if (photo) { + ensureDataMediaCreated(photo); } auto animatedThumb = document && document->isAnimation(); if (animatedThumb) { - _dataMedia->automaticLoad(fileOrigin(), nullptr); + _documentMedia->automaticLoad(fileOrigin(), nullptr); - bool loaded = _dataMedia->loaded(), loading = document->loading(), displayLoading = document->displayLoading(); + bool loaded = _documentMedia->loaded(), loading = document->loading(), displayLoading = document->displayLoading(); if (loaded && !_gif && !_gif.isBad()) { auto that = const_cast<Game*>(this); - that->_gif = Media::Clip::MakeReader(_dataMedia.get(), FullMsgId(), [that](Media::Clip::Notification notification) { + that->_gif = Media::Clip::MakeReader(_documentMedia.get(), FullMsgId(), [that](Media::Clip::Notification notification) { that->clipCallback(notification); }); } @@ -1383,7 +1426,7 @@ void Game::paint(Painter &p, const QRect &clip, const PaintContext *context) con }); } if (!_radial->animating()) { - _radial->start(_dataMedia->progress()); + _radial->start(_documentMedia->progress()); } } radial = isRadialAnimation(); @@ -1445,22 +1488,33 @@ TextState Game::getState( } void Game::prepareThumbnail(QSize size) const { - if (const auto photo = getResultPhoto()) { - validateThumbnail(photo->thumbnail(), size, true); - validateThumbnail(photo->thumbnailInline(), size, false); - } else if (const auto document = getResultDocument()) { - Assert(_dataMedia != nullptr); - validateThumbnail(_dataMedia->thumbnail(), size, true); - validateThumbnail(_dataMedia->thumbnailInline(), size, false); + if (const auto document = getResultDocument()) { + Assert(_documentMedia != nullptr); + validateThumbnail(_documentMedia->thumbnail(), size, true); + validateThumbnail(_documentMedia->thumbnailInline(), size, false); + } else if (const auto photo = getResultPhoto()) { + using Data::PhotoSize; + Assert(_photoMedia != nullptr); + validateThumbnail(_photoMedia->image(PhotoSize::Thumbnail), size, true); + validateThumbnail(_photoMedia->image(PhotoSize::Small), size, false); + validateThumbnail(_photoMedia->thumbnailInline(), size, false); } } void Game::ensureDataMediaCreated(not_null<DocumentData*> document) const { - if (_dataMedia) { + if (_documentMedia) { return; } - _dataMedia = document->createMediaView(); - _dataMedia->thumbnailWanted(fileOrigin()); + _documentMedia = document->createMediaView(); + _documentMedia->thumbnailWanted(fileOrigin()); +} + +void Game::ensureDataMediaCreated(not_null<PhotoData*> photo) const { + if (_photoMedia) { + return; + } + _photoMedia = photo->createMediaView(); + _photoMedia->wanted(Data::PhotoSize::Thumbnail, fileOrigin()); } void Game::validateThumbnail(Image *image, QSize size, bool good) const { @@ -1507,7 +1561,7 @@ bool Game::isRadialAnimation() const { return true; } else { ensureDataMediaCreated(getResultDocument()); - if (_dataMedia->loaded()) { + if (_documentMedia->loaded()) { _radial = nullptr; } } @@ -1520,22 +1574,28 @@ void Game::radialAnimationCallback(crl::time now) const { ensureDataMediaCreated(document); const auto updated = [&] { return _radial->update( - _dataMedia->progress(), - !document->loading() || _dataMedia->loaded(), + _documentMedia->progress(), + !document->loading() || _documentMedia->loaded(), now); }(); if (!anim::Disabled() || updated) { update(); } - if (!_radial->animating() && _dataMedia->loaded()) { + if (!_radial->animating() && _documentMedia->loaded()) { _radial = nullptr; } } void Game::unloadHeavyPart() { _gif.reset(); - getResultDocument()->unload(); - _dataMedia = nullptr; + if (const auto document = getResultDocument()) { + document->unload(); + _documentMedia = nullptr; + } + if (const auto photo = getResultPhoto()) { + photo->unload(); + _photoMedia = nullptr; + } } void Game::clipCallback(Media::Clip::Notification notification) { diff --git a/Telegram/SourceFiles/inline_bots/inline_bot_layout_internal.h b/Telegram/SourceFiles/inline_bots/inline_bot_layout_internal.h index dcc9cb6a3..781eede49 100644 --- a/Telegram/SourceFiles/inline_bots/inline_bot_layout_internal.h +++ b/Telegram/SourceFiles/inline_bots/inline_bot_layout_internal.h @@ -19,6 +19,7 @@ class SinglePlayer; } // namespace Lottie namespace Data { +class PhotoMedia; class DocumentMedia; } // namespace Data @@ -29,8 +30,9 @@ namespace internal { class FileBase : public ItemBase { public: FileBase(not_null<Context*> context, not_null<Result*> result); - // for saved gif layouts - FileBase(not_null<Context*> context, DocumentData *doc); + + // For saved gif layouts. + FileBase(not_null<Context*> context, not_null<DocumentData*> document); protected: DocumentData *getShownDocument() const; @@ -54,10 +56,13 @@ private: }; -class Gif : public FileBase { +class Gif final : public FileBase { public: - Gif(not_null<Context*> context, Result *result); - Gif(not_null<Context*> context, DocumentData *doc, bool hasDeleteButton); + Gif(not_null<Context*> context, not_null<Result*> result); + Gif( + not_null<Context*> context, + not_null<DocumentData*> document, + bool hasDeleteButton); void setPosition(int32 position) override; void initDimensions() override; @@ -131,9 +136,9 @@ private: class Photo : public ItemBase { public: - Photo(not_null<Context*> context, Result *result); + Photo(not_null<Context*> context, not_null<Result*> result); // Not used anywhere currently. - //Photo(not_null<Context*> context, PhotoData *photo); + //Photo(not_null<Context*> context, not_null<PhotoData*> photo); void initDimensions() override; @@ -149,6 +154,8 @@ public: QPoint point, StateRequest request) const override; + void unloadHeavyPart() override; + private: PhotoData *getShownPhoto() const; @@ -163,14 +170,16 @@ private: QSize frame, bool good) const; + mutable std::shared_ptr<Data::PhotoMedia> _photoMedia; + }; class Sticker : public FileBase { public: - Sticker(not_null<Context*> context, Result *result); + Sticker(not_null<Context*> context, not_null<Result*> result); ~Sticker(); // Not used anywhere currently. - //Sticker(not_null<Context*> context, DocumentData *document); + //Sticker(not_null<Context*> context, not_null<DocumentData*> document); void initDimensions() override; @@ -190,6 +199,8 @@ public: // ClickHandlerHost interface void clickHandlerActiveChanged(const ClickHandlerPtr &p, bool active) override; + void unloadHeavyPart() override; + private: void ensureDataMediaCreated(not_null<DocumentData*> document) const; void setupLottie() const; @@ -210,7 +221,7 @@ private: class Video : public FileBase { public: - Video(not_null<Context*> context, Result *result); + Video(not_null<Context*> context, not_null<Result*> result); void initDimensions() override; @@ -219,6 +230,8 @@ public: QPoint point, StateRequest request) const override; + void unloadHeavyPart() override; + private: ClickHandlerPtr _link; @@ -262,6 +275,7 @@ private: class File : public FileBase { public: File(not_null<Context*> context, not_null<Result*> result); + ~File(); void initDimensions() override; @@ -273,7 +287,7 @@ public: // ClickHandlerHost interface void clickHandlerActiveChanged(const ClickHandlerPtr &p, bool active) override; - ~File(); + void unloadHeavyPart() override; private: void thumbAnimationCallback(); @@ -334,7 +348,7 @@ private: class Contact : public ItemBase { public: - Contact(not_null<Context*> context, Result *result); + Contact(not_null<Context*> context, not_null<Result*> result); void initDimensions() override; @@ -353,7 +367,7 @@ private: class Article : public ItemBase { public: - Article(not_null<Context*> context, Result *result, bool withThumb); + Article(not_null<Context*> context, not_null<Result*> result, bool withThumb); void initDimensions() override; int resizeGetHeight(int width) override; @@ -378,7 +392,7 @@ private: class Game : public ItemBase { public: - Game(not_null<Context*> context, Result *result); + Game(not_null<Context*> context, not_null<Result*> result); void setPosition(int32 position) override; void initDimensions() override; @@ -391,6 +405,7 @@ public: void unloadHeavyPart() override; private: + void ensureDataMediaCreated(not_null<PhotoData*> photo) const; void ensureDataMediaCreated(not_null<DocumentData*> document) const; void countFrameSize(); @@ -403,7 +418,8 @@ private: void clipCallback(Media::Clip::Notification notification); Media::Clip::ReaderPointer _gif; - mutable std::shared_ptr<Data::DocumentMedia> _dataMedia; + mutable std::shared_ptr<Data::PhotoMedia> _photoMedia; + mutable std::shared_ptr<Data::DocumentMedia> _documentMedia; mutable QPixmap _thumb; mutable bool _thumbGood = false; mutable std::unique_ptr<Ui::RadialAnimation> _radial; diff --git a/Telegram/SourceFiles/inline_bots/inline_bot_layout_item.cpp b/Telegram/SourceFiles/inline_bots/inline_bot_layout_item.cpp index 6fbd3c0d5..0173e3b38 100644 --- a/Telegram/SourceFiles/inline_bots/inline_bot_layout_item.cpp +++ b/Telegram/SourceFiles/inline_bots/inline_bot_layout_item.cpp @@ -41,7 +41,7 @@ Result *ItemBase::getResult() const { } DocumentData *ItemBase::getDocument() const { - return _doc; + return _document; } PhotoData *ItemBase::getPhoto() const { @@ -49,8 +49,8 @@ PhotoData *ItemBase::getPhoto() const { } DocumentData *ItemBase::getPreviewDocument() const { - if (_doc) { - return _doc; + if (_document) { + return _document; } else if (_result) { return _result->_document; } @@ -60,8 +60,7 @@ DocumentData *ItemBase::getPreviewDocument() const { PhotoData *ItemBase::getPreviewPhoto() const { if (_photo) { return _photo; - } - if (_result) { + } else if (_result) { return _result->_photo; } return nullptr; @@ -71,16 +70,16 @@ void ItemBase::preload() const { const auto origin = fileOrigin(); if (_result) { if (_result->_photo) { - _result->_photo->loadThumbnail(origin); + _result->_photo->load(Data::PhotoSize::Thumbnail, origin); } else if (_result->_document) { _result->_document->loadThumbnail(origin); } else if (!_result->_thumb->isNull()) { _result->_thumb->load(origin); } - } else if (_doc) { - _doc->loadThumbnail(origin); + } else if (_document) { + _document->loadThumbnail(origin); } else if (_photo) { - _photo->loadThumbnail(origin); + _photo->load(Data::PhotoSize::Thumbnail, origin); } } @@ -96,7 +95,10 @@ void ItemBase::layoutChanged() { } } -std::unique_ptr<ItemBase> ItemBase::createLayout(not_null<Context*> context, Result *result, bool forceThumb) { +std::unique_ptr<ItemBase> ItemBase::createLayout( + not_null<Context*> context, + not_null<Result*> result, + bool forceThumb) { using Type = Result::Type; switch (result->_type) { @@ -126,7 +128,9 @@ std::unique_ptr<ItemBase> ItemBase::createLayout(not_null<Context*> context, Res return nullptr; } -std::unique_ptr<ItemBase> ItemBase::createLayoutGif(not_null<Context*> context, DocumentData *document) { +std::unique_ptr<ItemBase> ItemBase::createLayoutGif( + not_null<Context*> context, + not_null<DocumentData*> document) { return std::make_unique<internal::Gif>(context, document, true); } @@ -140,9 +144,7 @@ PhotoData *ItemBase::getResultPhoto() const { Image *ItemBase::getResultThumb() const { if (_result) { - if (_result->_photo) { - return _result->_photo->thumbnail(); - } else if (!_result->_thumb->isNull()) { + if (!_result->_thumb->isNull()) { return _result->_thumb.get(); } else if (!_result->_locationThumb->isNull()) { return _result->_locationThumb.get(); diff --git a/Telegram/SourceFiles/inline_bots/inline_bot_layout_item.h b/Telegram/SourceFiles/inline_bots/inline_bot_layout_item.h index 86e6e0b5c..a630e8308 100644 --- a/Telegram/SourceFiles/inline_bots/inline_bot_layout_item.h +++ b/Telegram/SourceFiles/inline_bots/inline_bot_layout_item.h @@ -50,8 +50,8 @@ public: : _result(result) , _context(context) { } - ItemBase(not_null<Context*> context, DocumentData *doc) - : _doc(doc) + ItemBase(not_null<Context*> context, not_null<DocumentData*> document) + : _document(document) , _context(context) { } // Not used anywhere currently. @@ -94,8 +94,13 @@ public: update(); } - static std::unique_ptr<ItemBase> createLayout(not_null<Context*> context, Result *result, bool forceThumb); - static std::unique_ptr<ItemBase> createLayoutGif(not_null<Context*> context, DocumentData *document); + static std::unique_ptr<ItemBase> createLayout( + not_null<Context*> context, + not_null<Result*> result, + bool forceThumb); + static std::unique_ptr<ItemBase> createLayoutGif( + not_null<Context*> context, + not_null<DocumentData*> document); protected: DocumentData *getResultDocument() const; @@ -114,7 +119,7 @@ protected: Data::FileOrigin fileOrigin() const; Result *_result = nullptr; - DocumentData *_doc = nullptr; + DocumentData *_document = nullptr; PhotoData *_photo = nullptr; ClickHandlerPtr _send = ClickHandlerPtr{ new SendClickHandler() }; diff --git a/Telegram/SourceFiles/inline_bots/inline_bot_result.cpp b/Telegram/SourceFiles/inline_bots/inline_bot_result.cpp index 040831799..781e2caff 100644 --- a/Telegram/SourceFiles/inline_bots/inline_bot_result.cpp +++ b/Telegram/SourceFiles/inline_bots/inline_bot_result.cpp @@ -12,6 +12,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "data/data_document.h" #include "data/data_session.h" #include "data/data_file_origin.h" +#include "data/data_photo_media.h" #include "data/data_document_media.h" #include "inline_bots/inline_bot_layout_item.h" #include "inline_bots/inline_bot_send_data.h" @@ -105,7 +106,9 @@ std::unique_ptr<Result> Result::create( if (result->_type == Type::Photo) { result->_photo = Auth().data().photoFromWeb( *content, - result->_thumb, + (imageThumb + ? Images::FromWebDocument(*r.vthumb()) + : ImageLocation()), true); } else { result->_document = Auth().data().documentFromWeb( @@ -276,10 +279,13 @@ std::unique_ptr<Result> Result::create( bool Result::onChoose(Layout::ItemBase *layout) { if (_photo && _type == Type::Photo) { - if (_photo->thumbnail()->loaded()) { + const auto media = _photo->activeMediaView(); + if (!media || media->image(Data::PhotoSize::Thumbnail)) { return true; - } else if (!_photo->thumbnail()->loading()) { - _photo->thumbnail()->loadEvenCancelled(Data::FileOrigin()); + } else if (!_photo->loading(Data::PhotoSize::Thumbnail)) { + _photo->load( + Data::PhotoSize::Thumbnail, + Data::FileOrigin()); } return false; } diff --git a/Telegram/SourceFiles/media/view/media_view_group_thumbs.cpp b/Telegram/SourceFiles/media/view/media_view_group_thumbs.cpp index c320a3492..f32008236 100644 --- a/Telegram/SourceFiles/media/view/media_view_group_thumbs.cpp +++ b/Telegram/SourceFiles/media/view/media_view_group_thumbs.cpp @@ -10,6 +10,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "data/data_shared_media.h" #include "data/data_user_photos.h" #include "data/data_photo.h" +#include "data/data_photo_media.h" #include "data/data_document.h" #include "data/data_document_media.h" #include "data/data_media_types.h" @@ -129,9 +130,10 @@ public: Dying, }; + Thumb(Key key, Fn<void()> handler); Thumb( Key key, - Image *image, + not_null<PhotoData*> photo, Data::FileOrigin origin, Fn<void()> handler); Thumb( @@ -165,6 +167,7 @@ private: ClickHandlerPtr _link; const Key _key; std::shared_ptr<Data::DocumentMedia> _documentMedia; + std::shared_ptr<Data::PhotoMedia> _photoMedia; Image *_image = nullptr; Data::FileOrigin _origin; State _state = State::Alive; @@ -178,18 +181,28 @@ private: }; +GroupThumbs::Thumb::Thumb(Key key, Fn<void()> handler) +: _key(key) { + _link = std::make_shared<LambdaClickHandler>(std::move(handler)); + _fullWidth = std::min( + wantedPixSize().width(), + st::mediaviewGroupWidthMax); + validateImage(); +} + GroupThumbs::Thumb::Thumb( Key key, - Image *image, + not_null<PhotoData*> photo, Data::FileOrigin origin, Fn<void()> handler) : _key(key) -, _image(image) +, _photoMedia(photo->createMediaView()) , _origin(origin) { _link = std::make_shared<LambdaClickHandler>(std::move(handler)); _fullWidth = std::min( wantedPixSize().width(), st::mediaviewGroupWidthMax); + _photoMedia->wanted(Data::PhotoSize::Thumbnail, origin); validateImage(); } @@ -218,8 +231,12 @@ QSize GroupThumbs::Thumb::wantedPixSize() const { } void GroupThumbs::Thumb::validateImage() { - if (!_image && _documentMedia) { - _image = _documentMedia->thumbnail(); + if (!_image) { + if (_photoMedia) { + _image = _photoMedia->image(Data::PhotoSize::Thumbnail); + } else if (_documentMedia) { + _image = _documentMedia->thumbnail(); + } } if (!_full.isNull() || !_image) { return; @@ -543,12 +560,12 @@ auto GroupThumbs::createThumb(Key key) -> std::unique_ptr<Thumb> { if (const auto photoId = base::get_if<PhotoId>(&key)) { const auto photo = Auth().data().photo(*photoId); - return createThumb(key, photo->thumbnail()); + return createThumb(key, photo); } else if (const auto msgId = base::get_if<FullMsgId>(&key)) { if (const auto item = Auth().data().message(*msgId)) { if (const auto media = item->media()) { if (const auto photo = media->photo()) { - return createThumb(key, photo->thumbnail()); + return createThumb(key, photo); } else if (const auto document = media->document()) { return createThumb(key, document); } @@ -583,18 +600,29 @@ auto GroupThumbs::createThumb( } const auto &item = collage.items[index]; if (const auto photo = base::get_if<PhotoData*>(&item)) { - return createThumb(key, (*photo)->thumbnail()); + return createThumb(key, (*photo)); } else if (const auto document = base::get_if<DocumentData*>(&item)) { return createThumb(key, (*document)); } return createThumb(key, nullptr); } -auto GroupThumbs::createThumb(Key key, Image *image) +auto GroupThumbs::createThumb(Key key, std::nullptr_t) -> std::unique_ptr<Thumb> { const auto weak = base::make_weak(this); const auto origin = ComputeFileOrigin(key, _context); - return std::make_unique<Thumb>(key, image, origin, [=] { + return std::make_unique<Thumb>(key, [=] { + if (const auto strong = weak.get()) { + strong->_activateStream.fire_copy(key); + } + }); +} + +auto GroupThumbs::createThumb(Key key, not_null<PhotoData*> photo) +-> std::unique_ptr<Thumb> { + const auto weak = base::make_weak(this); + const auto origin = ComputeFileOrigin(key, _context); + return std::make_unique<Thumb>(key, photo, origin, [=] { if (const auto strong = weak.get()) { strong->_activateStream.fire_copy(key); } diff --git a/Telegram/SourceFiles/media/view/media_view_group_thumbs.h b/Telegram/SourceFiles/media/view/media_view_group_thumbs.h index 3126d1ba3..94937bfc9 100644 --- a/Telegram/SourceFiles/media/view/media_view_group_thumbs.h +++ b/Telegram/SourceFiles/media/view/media_view_group_thumbs.h @@ -100,10 +100,11 @@ private: Key key, const WebPageCollage &collage, int index); - std::unique_ptr<Thumb> createThumb(Key key, Image *image); + std::unique_ptr<Thumb> createThumb(Key key, not_null<PhotoData*> photo); std::unique_ptr<Thumb> createThumb( Key key, not_null<DocumentData*> document); + std::unique_ptr<Thumb> createThumb(Key key, std::nullptr_t); void update(); void countUpdatedRect(); diff --git a/Telegram/SourceFiles/media/view/media_view_overlay_widget.cpp b/Telegram/SourceFiles/media/view/media_view_overlay_widget.cpp index 9e68a0913..23979583a 100644 --- a/Telegram/SourceFiles/media/view/media_view_overlay_widget.cpp +++ b/Telegram/SourceFiles/media/view/media_view_overlay_widget.cpp @@ -40,6 +40,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "data/data_user.h" #include "data/data_file_origin.h" #include "data/data_media_rotation.h" +#include "data/data_photo_media.h" #include "data/data_document_media.h" #include "window/themes/window_theme_preview.h" #include "window/window_peer_menu.h" @@ -449,14 +450,14 @@ QSize OverlayWidget::videoSize() const { } bool OverlayWidget::videoIsGifv() const { - return _streamed && _doc->isAnimation() && !_doc->isVideoMessage(); + return _streamed && _document->isAnimation() && !_document->isVideoMessage(); } QImage OverlayWidget::videoFrame() const { Expects(videoShown()); auto request = Streaming::FrameRequest(); - //request.radius = (_doc && _doc->isVideoMessage()) + //request.radius = (_document && _document->isVideoMessage()) // ? ImageRoundRadius::Ellipse // : ImageRoundRadius::None; return _streamed->instance.player().ready() @@ -504,21 +505,21 @@ QImage OverlayWidget::videoFrameForDirectPaint() const { } bool OverlayWidget::documentContentShown() const { - return _doc && (!_staticContent.isNull() || videoShown()); + return _document && (!_staticContent.isNull() || videoShown()); } bool OverlayWidget::documentBubbleShown() const { - return (!_photo && !_doc) - || (_doc + return (!_photo && !_document) + || (_document && !_themePreviewShown && !_streamed && _staticContent.isNull()); } void OverlayWidget::clearStreaming(bool savePosition) { - if (_streamed && _doc && savePosition) { + if (_streamed && _document && savePosition) { Media::Player::SaveLastPlaybackPosition( - _doc, + _document, _streamed->instance.player().prepareLegacyState()); } _fullScreenVideo = false; @@ -526,21 +527,21 @@ void OverlayWidget::clearStreaming(bool savePosition) { } void OverlayWidget::documentUpdated(DocumentData *doc) { - if (_doc && _doc == doc) { + if (_document && _document == doc) { if (documentBubbleShown()) { - if ((_doc->loading() && _docCancel->isHidden()) || (!_doc->loading() && !_docCancel->isHidden())) { + if ((_document->loading() && _docCancel->isHidden()) || (!_document->loading() && !_docCancel->isHidden())) { updateControls(); - } else if (_doc->loading()) { + } else if (_document->loading()) { updateDocSize(); update(_docRect); } } else if (_streamed) { - const auto ready = _docMedia->loaded() - ? _doc->size - : _doc->loading() - ? std::clamp(_doc->loadOffset(), 0, _doc->size) + const auto ready = _documentMedia->loaded() + ? _document->size + : _document->loading() + ? std::clamp(_document->loadOffset(), 0, _document->size) : 0; - _streamed->controls.setLoadingProgress(ready, _doc->size); + _streamed->controls.setLoadingProgress(ready, _document->size); } } } @@ -553,10 +554,10 @@ void OverlayWidget::changingMsgId(not_null<HistoryItem*> row, MsgId newId) { } void OverlayWidget::updateDocSize() { - if (!_doc || !documentBubbleShown()) return; + if (!_document || !documentBubbleShown()) return; - if (_doc->loading()) { - quint64 ready = _doc->loadOffset(), total = _doc->size; + if (_document->loading()) { + quint64 ready = _document->loadOffset(), total = _document->size; QString readyStr, totalStr, mb; if (total >= 1024 * 1024) { // more than 1 mb qint64 readyTenthMb = (ready * 10 / (1024 * 1024)), totalTenthMb = (total * 10 / (1024 * 1024)); @@ -575,7 +576,7 @@ void OverlayWidget::updateDocSize() { } _docSize = tr::lng_media_save_progress(tr::now, lt_ready, readyStr, lt_total, totalStr, lt_mb, mb); } else { - _docSize = formatSizeText(_doc->size); + _docSize = formatSizeText(_document->size); } _docSizeWidth = st::mediaviewFont->width(_docSize); int32 maxw = st::mediaviewFileSize.width() - st::mediaviewFileIconSize - st::mediaviewFilePadding * 3; @@ -602,14 +603,14 @@ void OverlayWidget::refreshNavVisibility() { } void OverlayWidget::updateControls() { - if (_doc && documentBubbleShown()) { - if (_doc->loading()) { + if (_document && documentBubbleShown()) { + if (_document->loading()) { _docDownload->hide(); _docSaveAs->hide(); _docCancel->moveToLeft(_docRect.x() + 2 * st::mediaviewFilePadding + st::mediaviewFileIconSize, _docRect.y() + st::mediaviewFilePadding + st::mediaviewFileLinksTop); _docCancel->show(); } else { - if (_docMedia->loaded(true)) { + if (_documentMedia->loaded(true)) { _docDownload->hide(); _docSaveAs->moveToLeft(_docRect.x() + 2 * st::mediaviewFilePadding + st::mediaviewFileIconSize, _docRect.y() + st::mediaviewFilePadding + st::mediaviewFileLinksTop); _docSaveAs->show(); @@ -632,10 +633,10 @@ void OverlayWidget::updateControls() { updateThemePreviewGeometry(); - _saveVisible = (_photo && _photo->loaded()) - || (_doc - && _doc->filepath(true).isEmpty() - && !_doc->loading()); + _saveVisible = (_photo && _photoMedia->loaded()) + || (_document + && _document->filepath(true).isEmpty() + && !_document->loading()); _saveNav = myrtlrect(width() - st::mediaviewIconSize.width() * 3, height() - st::mediaviewIconSize.height(), st::mediaviewIconSize.width(), st::mediaviewIconSize.height()); _saveNavIcon = style::centerrect(_saveNav, st::mediaviewSave); _rotateNav = myrtlrect(width() - st::mediaviewIconSize.width() * 2, height() - st::mediaviewIconSize.height(), st::mediaviewIconSize.width(), st::mediaviewIconSize.height()); @@ -649,8 +650,8 @@ void OverlayWidget::updateControls() { return ItemDateTime(item); } else if (_photo) { return base::unixtime::parse(_photo->date); - } else if (_doc) { - return base::unixtime::parse(_doc->date); + } else if (_document) { + return base::unixtime::parse(_document->date); } return dNow; }(); @@ -733,16 +734,16 @@ void OverlayWidget::refreshCaptionGeometry() { void OverlayWidget::updateActions() { _actions.clear(); - if (_doc && _doc->loading()) { + if (_document && _document->loading()) { _actions.push_back({ tr::lng_cancel(tr::now), SLOT(onSaveCancel()) }); } if (IsServerMsgId(_msgid.msg)) { _actions.push_back({ tr::lng_context_to_msg(tr::now), SLOT(onToMessage()) }); } - if (_doc && !_doc->filepath(true).isEmpty()) { + if (_document && !_document->filepath(true).isEmpty()) { _actions.push_back({ Platform::IsMac() ? tr::lng_context_show_in_finder(tr::now) : tr::lng_context_show_in_folder(tr::now), SLOT(onShowInFolder()) }); } - if ((_doc && documentContentShown()) || (_photo && _photo->loaded())) { + if ((_document && documentContentShown()) || (_photo && _photoMedia->loaded())) { _actions.push_back({ tr::lng_mediaview_copy(tr::now), SLOT(onCopy()) }); } if (_photo && _photo->hasSticker) { @@ -771,7 +772,7 @@ void OverlayWidget::updateActions() { _actions.push_back({ tr::lng_mediaview_save_as(tr::now), SLOT(onSaveAs()) }); if (const auto overviewType = computeOverviewType()) { - _actions.push_back({ _doc ? tr::lng_mediaview_files_all(tr::now) : tr::lng_mediaview_photos_all(tr::now), SLOT(onOverview()) }); + _actions.push_back({ _document ? tr::lng_mediaview_files_all(tr::now) : tr::lng_mediaview_photos_all(tr::now), SLOT(onOverview()) }); } } @@ -783,7 +784,7 @@ auto OverlayWidget::computeOverviewType() const } else if (mediaType == SharedMediaType::PhotoVideo) { if (_photo) { return SharedMediaOverviewType(SharedMediaType::Photo); - } else if (_doc) { + } else if (_document) { return SharedMediaOverviewType(SharedMediaType::Video); } } @@ -926,19 +927,19 @@ void OverlayWidget::resizeContentByScreenSize() { } float64 OverlayWidget::radialProgress() const { - if (_doc) { - return _docMedia->progress(); + if (_document) { + return _documentMedia->progress(); } else if (_photo) { - return _photo->large()->progress(); + return _photoMedia->progress(); } return 1.; } bool OverlayWidget::radialLoading() const { - if (_doc) { - return _doc->loading() && !_streamed; + if (_document) { + return _document->loading() && !_streamed; } else if (_photo) { - return _photo->large()->loading(); + return _photo->loading(); } return false; } @@ -946,7 +947,7 @@ bool OverlayWidget::radialLoading() const { QRect OverlayWidget::radialRect() const { if (_photo) { return _photoRadialRect; - } else if (_doc) { + } else if (_document) { return QRect( QPoint( _docIconRect.x() + ((_docIconRect.width() - st::radialSize.width()) / 2), @@ -970,7 +971,7 @@ crl::time OverlayWidget::radialTimeShift() const { } bool OverlayWidget::radialAnimationCallback(crl::time now) { - if ((!_doc && !_photo) || _streamed) { + if ((!_document && !_photo) || _streamed) { return false; } const auto wasAnimating = _radial.animating(); @@ -982,17 +983,17 @@ bool OverlayWidget::radialAnimationCallback(crl::time now) { && (!anim::Disabled() || updated)) { update(radialRect()); } - const auto ready = _doc && _docMedia->loaded(); - const auto streamVideo = ready && _docMedia->canBePlayed(); - const auto tryOpenImage = ready && (_doc->size < App::kImageSizeLimit); + const auto ready = _document && _documentMedia->loaded(); + const auto streamVideo = ready && _documentMedia->canBePlayed(); + const auto tryOpenImage = ready && (_document->size < App::kImageSizeLimit); if (ready && ((tryOpenImage && !_radial.animating()) || streamVideo)) { _streamingStartPaused = false; if (streamVideo) { redisplayContent(); } else { - auto &location = _doc->location(true); + auto &location = _document->location(true); if (location.accessEnable()) { - if (_doc->isTheme() + if (_document->isTheme() || QImageReader(location.name()).canRead()) { redisplayContent(); } @@ -1091,9 +1092,7 @@ void OverlayWidget::clearData() { setContext(std::nullopt); _from = nullptr; _fromName = QString(); - _photo = nullptr; - _doc = nullptr; - _docMedia = nullptr; + assignMediaPointer(nullptr); _pip = nullptr; _fullScreenVideo = false; _caption.clear(); @@ -1103,6 +1102,31 @@ OverlayWidget::~OverlayWidget() { delete base::take(_menu); } +void OverlayWidget::assignMediaPointer(DocumentData *document) { + _photo = nullptr; + _photoMedia = nullptr; + if (_document != document) { + if ((_document = document)) { + _documentMedia = _document->createMediaView(); + _documentMedia->goodThumbnailWanted(); + _documentMedia->thumbnailWanted(fileOrigin()); + } else { + _documentMedia = nullptr; + } + } +} + +void OverlayWidget::assignMediaPointer(not_null<PhotoData*> photo) { + _document = nullptr; + _documentMedia = nullptr; + if (_photo != photo) { + _photo = photo; + _photoMedia = _photo->createMediaView(); + _photoMedia->wanted(Data::PhotoSize::Large, fileOrigin()); + _photoMedia->wanted(Data::PhotoSize::Small, fileOrigin()); + } +} + void OverlayWidget::clickHandlerActiveChanged(const ClickHandlerPtr &p, bool active) { setCursor((active || ClickHandler::getPressed()) ? style::cur_pointer : style::cur_default); update(QRegion(_saveMsg) + _captionRect); @@ -1219,14 +1243,14 @@ void OverlayWidget::notifyFileDialogShown(bool shown) { void OverlayWidget::onSaveAs() { QString file; - if (_doc) { - const auto &location = _doc->location(true); - const auto bytes = _docMedia->bytes(); + if (_document) { + const auto &location = _document->location(true); + const auto bytes = _documentMedia->bytes(); if (!bytes.isEmpty() || location.accessEnable()) { QFileInfo alreadyInfo(location.name()); QDir alreadyDir(alreadyInfo.dir()); QString name = alreadyInfo.fileName(), filter; - const auto mimeType = Core::MimeTypeForName(_doc->mimeString()); + const auto mimeType = Core::MimeTypeForName(_document->mimeString()); QStringList p = mimeType.globPatterns(); QString pattern = p.isEmpty() ? QString() : p.front(); if (name.isEmpty()) { @@ -1257,14 +1281,15 @@ void OverlayWidget::onSaveAs() { } else { DocumentSaveClickHandler::Save( fileOrigin(), - _doc, + _document, DocumentSaveClickHandler::Mode::ToNewFile); updateControls(); updateOver(_lastMouseMovePos); } } else { - if (!_photo || !_photo->loaded()) return; + if (!_photo || !_photoMedia->loaded()) return; + const auto image = _photoMedia->image(Data::PhotoSize::Large)->original(); auto filter = qsl("JPEG Image (*.jpg);;") + FileDialog::AllFilesFilter(); FileDialog::GetWritePath( this, @@ -1277,8 +1302,8 @@ void OverlayWidget::onSaveAs() { false, _photo->date), crl::guard(this, [=, photo = _photo](const QString &result) { - if (!result.isEmpty() && _photo == photo && photo->loaded()) { - photo->large()->original().save(result, "JPG"); + if (!result.isEmpty() && _photo == photo) { + image.save(result, "JPG"); } })); } @@ -1288,15 +1313,15 @@ void OverlayWidget::onSaveAs() { } void OverlayWidget::onDocClick() { - if (_doc->loading()) { + if (_document->loading()) { onSaveCancel(); } else { DocumentOpenClickHandler::Open( fileOrigin(), - _doc, + _document, Auth().data().message(_msgid)); - if (_doc->loading() && !_radial.animating()) { - _radial.start(_docMedia->progress()); + if (_document->loading() && !_radial.animating()) { + _radial.start(_documentMedia->progress()); } } } @@ -1319,12 +1344,12 @@ void OverlayWidget::onDownload() { path = Global::DownloadPath(); } QString toName; - if (_doc) { - const auto &location = _doc->location(true); + if (_document) { + const auto &location = _document->location(true); if (location.accessEnable()) { if (!QDir().exists(path)) QDir().mkpath(path); toName = filedialogNextFilename( - _doc->filename(), + _document->filename(), location.name(), path); if (!toName.isEmpty() && toName != location.name()) { @@ -1335,11 +1360,11 @@ void OverlayWidget::onDownload() { } location.accessDisable(); } else { - if (_doc->filepath(true).isEmpty() - && !_doc->loading()) { + if (_document->filepath(true).isEmpty() + && !_document->loading()) { DocumentSaveClickHandler::Save( fileOrigin(), - _doc, + _document, DocumentSaveClickHandler::Mode::ToFile); updateControls(); } else { @@ -1349,13 +1374,18 @@ void OverlayWidget::onDownload() { updateOver(_lastMouseMovePos); } } else { - if (!_photo || !_photo->loaded()) { + if (!_photo || !_photoMedia->loaded()) { _saveVisible = false; update(_saveNav); } else { - if (!QDir().exists(path)) QDir().mkpath(path); + const auto image = _photoMedia->image( + Data::PhotoSize::Large)->original(); + + if (!QDir().exists(path)) { + QDir().mkpath(path); + } toName = filedialogDefaultName(qsl("photo"), qsl(".jpg"), path); - if (!_photo->large()->original().save(toName, "JPG")) { + if (!image.save(toName, "JPG")) { toName = QString(); } } @@ -1369,18 +1399,18 @@ void OverlayWidget::onDownload() { } void OverlayWidget::onSaveCancel() { - if (_doc && _doc->loading()) { - _doc->cancel(); - if (_docMedia->canBePlayed()) { + if (_document && _document->loading()) { + _document->cancel(); + if (_documentMedia->canBePlayed()) { redisplayContent(); } } } void OverlayWidget::onShowInFolder() { - if (!_doc) return; + if (!_document) return; - auto filepath = _doc->filepath(true); + auto filepath = _document->filepath(true); if (!filepath.isEmpty()) { File::ShowInFolder(filepath); close(); @@ -1432,12 +1462,14 @@ void OverlayWidget::onOverview() { void OverlayWidget::onCopy() { _dropdown->hideAnimated(Ui::DropdownMenu::HideOption::IgnoreShow); - if (_doc) { + if (_document) { QGuiApplication::clipboard()->setImage(videoShown() ? transformVideoFrame(videoFrame()) : transformStaticContent(_staticContent)); - } else if (_photo && _photo->loaded()) { - QGuiApplication::clipboard()->setPixmap(_photo->large()->pix(fileOrigin())); + } else if (_photo && _photoMedia->loaded()) { + const auto image = _photoMedia->image( + Data::PhotoSize::Large)->original(); + QGuiApplication::clipboard()->setImage(image); } } @@ -1459,10 +1491,10 @@ std::optional<OverlayWidget::SharedMediaType> OverlayWidget::sharedMediaType() c return Type::PhotoVideo; } return Type::ChatPhoto; - } else if (_doc) { - if (_doc->isGifv()) { + } else if (_document) { + if (_document->isGifv()) { return Type::GIF; - } else if (_doc->isVideoFile()) { + } else if (_document->isVideoFile()) { return Type::PhotoVideo; } return Type::File; @@ -1563,7 +1595,7 @@ void OverlayWidget::validateSharedMedia() { } void OverlayWidget::handleSharedMediaUpdate(SharedMediaWithLastSlice &&update) { - if ((!_photo && !_doc) || !_sharedMedia) { + if ((!_photo && !_document) || !_sharedMedia) { _sharedMediaData = std::nullopt; _sharedMediaDataKey = std::nullopt; } else { @@ -1640,7 +1672,7 @@ std::optional<OverlayWidget::CollageKey> OverlayWidget::collageKey() const { if (const auto media = item->media()) { if (const auto page = media->webpage()) { for (const auto &item : page->collage.items) { - if (item == _photo || item == _doc) { + if (item == _photo || item == _document) { return item; } } @@ -1749,10 +1781,10 @@ void OverlayWidget::refreshCaption(HistoryItem *item) { using namespace HistoryView; _caption = Ui::Text::String(st::msgMinWidth); const auto duration = (_streamed && !videoIsGifv()) - ? _doc->getDuration() + ? _document->getDuration() : 0; const auto base = duration - ? DocumentTimestampLinkBase(_doc, item->fullId()) + ? DocumentTimestampLinkBase(_document, item->fullId()) : QString(); _caption.setMarkedText( st::mediaviewCaptionStyle, @@ -1850,7 +1882,7 @@ void OverlayWidget::showPhoto(not_null<PhotoData*> photo, HistoryItem *context) clearControlsState(); _firstOpenedPeerPhoto = false; - _photo = photo; + assignMediaPointer(photo); refreshMediaViewer(); @@ -1864,7 +1896,7 @@ void OverlayWidget::showPhoto(not_null<PhotoData*> photo, not_null<PeerData*> co clearControlsState(); _firstOpenedPeerPhoto = true; - _photo = photo; + assignMediaPointer(photo); refreshMediaViewer(); @@ -1897,7 +1929,6 @@ void OverlayWidget::showDocument( } clearControlsState(); - _photo = nullptr; _streamingStartPaused = false; displayDocument(document, context, cloud, continueStreaming); @@ -1916,10 +1947,9 @@ void OverlayWidget::displayPhoto(not_null<PhotoData*> photo, HistoryItem *item) clearStreaming(); destroyThemePreview(); - _doc = nullptr; - _docMedia = nullptr; + _fullScreenVideo = false; - _photo = photo; + assignMediaPointer(photo); _rotation = _photo->owner().mediaRotation().get(_photo); _radial.stop(); @@ -1938,7 +1968,6 @@ void OverlayWidget::displayPhoto(not_null<PhotoData*> photo, HistoryItem *item) _h = size.height(); contentSizeChanged(); refreshFromLabel(item); - _photo->download(fileOrigin()); displayFinished(); } @@ -1959,7 +1988,7 @@ void OverlayWidget::redisplayContent() { if (_photo) { displayPhoto(_photo, item); } else { - displayDocument(_doc, item); + displayDocument(_document, item); } } @@ -1974,42 +2003,37 @@ void OverlayWidget::displayDocument( } _fullScreenVideo = false; _staticContent = QPixmap(); - clearStreaming(_doc != doc); + clearStreaming(_document != doc); destroyThemePreview(); - _doc = doc; - if (_doc) { - _docMedia = _doc->createMediaView(); - _docMedia->goodThumbnailWanted(); - _docMedia->thumbnailWanted(fileOrigin()); - } - _rotation = _doc ? _doc->owner().mediaRotation().get(_doc) : 0; + assignMediaPointer(doc); + + _rotation = _document ? _document->owner().mediaRotation().get(_document) : 0; _themeCloudData = cloud; - _photo = nullptr; _radial.stop(); refreshMediaViewer(); - if (_doc) { - if (_doc->sticker()) { - if (const auto image = _docMedia->getStickerLarge()) { + if (_document) { + if (_document->sticker()) { + if (const auto image = _documentMedia->getStickerLarge()) { _staticContent = image->pix(fileOrigin()); - } else if (const auto thumbnail = _docMedia->thumbnail()) { + } else if (const auto thumbnail = _documentMedia->thumbnail()) { _staticContent = thumbnail->pixBlurred( fileOrigin(), - _doc->dimensions.width(), - _doc->dimensions.height()); + _document->dimensions.width(), + _document->dimensions.height()); } } else { - if (_docMedia->canBePlayed() + if (_documentMedia->canBePlayed() && initStreaming(continueStreaming)) { - } else if (_doc->isVideoFile()) { - _docMedia->automaticLoad(fileOrigin(), item); + } else if (_document->isVideoFile()) { + _documentMedia->automaticLoad(fileOrigin(), item); initStreamingThumbnail(); - } else if (_doc->isTheme()) { - _docMedia->automaticLoad(fileOrigin(), item); + } else if (_document->isTheme()) { + _documentMedia->automaticLoad(fileOrigin(), item); initThemePreview(); } else { - _docMedia->automaticLoad(fileOrigin(), item); - auto &location = _doc->location(true); + _documentMedia->automaticLoad(fileOrigin(), item); + auto &location = _document->location(true); if (location.accessEnable()) { const auto &path = location.name(); if (QImageReader(path).canRead()) { @@ -2024,8 +2048,8 @@ void OverlayWidget::displayDocument( _docIconRect = QRect((width() - st::mediaviewFileIconSize) / 2, (height() - st::mediaviewFileIconSize) / 2, st::mediaviewFileIconSize, st::mediaviewFileIconSize); if (documentBubbleShown()) { - if (!_doc || !_doc->hasThumbnail()) { - int32 colorIndex = documentColorIndex(_doc, _docExt); + if (!_document || !_document->hasThumbnail()) { + int32 colorIndex = documentColorIndex(_document, _docExt); _docIconColor = documentColor(colorIndex); const style::icon *(thumbs[]) = { &st::mediaviewFileBlue, &st::mediaviewFileGreen, &st::mediaviewFileRed, &st::mediaviewFileYellow }; _docIcon = thumbs[colorIndex]; @@ -2037,9 +2061,9 @@ void OverlayWidget::displayDocument( _docExtWidth = st::mediaviewFileExtFont->width(_docExt); } } else { - _doc->loadThumbnail(fileOrigin()); - const auto tw = _docMedia->thumbnailSize().width(); - const auto th = _docMedia->thumbnailSize().height(); + _document->loadThumbnail(fileOrigin()); + const auto tw = _documentMedia->thumbnailSize().width(); + const auto th = _documentMedia->thumbnailSize().height(); if (!tw || !th) { _docThumbx = _docThumby = _docThumbw = 0; } else if (tw > th) { @@ -2055,14 +2079,14 @@ void OverlayWidget::displayDocument( int32 maxw = st::mediaviewFileSize.width() - st::mediaviewFileIconSize - st::mediaviewFilePadding * 3; - if (_doc) { - _docName = (_doc->type == StickerDocument) + if (_document) { + _docName = (_document->type == StickerDocument) ? tr::lng_in_dlg_sticker(tr::now) - : (_doc->type == AnimatedDocument + : (_document->type == AnimatedDocument ? qsl("GIF") - : (_doc->filename().isEmpty() + : (_document->filename().isEmpty() ? tr::lng_mediaview_doc_image(tr::now) - : _doc->filename())); + : _document->filename())); } else { _docName = tr::lng_message_empty(tr::now); } @@ -2135,15 +2159,15 @@ void OverlayWidget::displayFinished() { } bool OverlayWidget::initStreaming(bool continueStreaming) { - Expects(_doc != nullptr); - Expects(_docMedia->canBePlayed()); + Expects(_document != nullptr); + Expects(_documentMedia->canBePlayed()); if (_streamed) { return true; } initStreamingThumbnail(); if (!createStreamingObjects()) { - _doc->setInappPlaybackFailed(); + _document->setInappPlaybackFailed(); return false; } @@ -2181,26 +2205,26 @@ void OverlayWidget::startStreamingPlayer() { return; } - const auto position = _doc - ? _doc->session().settings().mediaLastPlaybackPosition(_doc->id) + const auto position = _document + ? _document->session().settings().mediaLastPlaybackPosition(_document->id) : 0; restartAtSeekPosition(position); } void OverlayWidget::initStreamingThumbnail() { - Expects(_doc != nullptr); + Expects(_document != nullptr); - const auto good = _docMedia->goodThumbnail(); + const auto good = _documentMedia->goodThumbnail(); const auto useGood = (good && good->loaded()); - const auto thumbnail = _docMedia->thumbnail(); + const auto thumbnail = _documentMedia->thumbnail(); const auto useThumb = (thumbnail != nullptr); - const auto blurred = _docMedia->thumbnailInline(); + const auto blurred = _documentMedia->thumbnailInline(); if (good && !useGood) { good->load({}); } else if (thumbnail) { thumbnail->load(fileOrigin()); } - const auto size = useGood ? good->size() : _doc->dimensions; + const auto size = useGood ? good->size() : _document->dimensions; if (!useGood && !thumbnail && !blurred) { return; } else if (size.isEmpty()) { @@ -2208,7 +2232,7 @@ void OverlayWidget::initStreamingThumbnail() { } const auto w = size.width(); const auto h = size.height(); - const auto options = VideoThumbOptions(_doc); + const auto options = VideoThumbOptions(_document); const auto goodOptions = (options & ~Images::Option::Blurred); _staticContent = (useGood ? good @@ -2245,7 +2269,7 @@ void OverlayWidget::applyVideoSize() { bool OverlayWidget::createStreamingObjects() { _streamed = std::make_unique<Streamed>( - _doc, + _document, fileOrigin(), this, static_cast<PlaybackControls::Delegate*>(this), @@ -2256,10 +2280,10 @@ bool OverlayWidget::createStreamingObjects() { } _streamed->instance.setPriority(kOverlayLoaderPriority); _streamed->instance.lockPlayer(); - _streamed->withSound = _doc->isAudioFile() - || _doc->isVideoFile() - || _doc->isVoiceMessage() - || _doc->isVideoMessage(); + _streamed->withSound = _document->isAudioFile() + || _document->isVideoFile() + || _document->isVoiceMessage() + || _document->isVideoMessage(); if (videoIsGifv()) { _streamed->controls.hide(); @@ -2316,14 +2340,14 @@ void OverlayWidget::handleStreamingUpdate(Streaming::Update &&update) { } void OverlayWidget::handleStreamingError(Streaming::Error &&error) { - Expects(_doc != nullptr); + Expects(_document != nullptr); if (error == Streaming::Error::NotStreamable) { - _doc->setNotSupportsStreaming(); + _document->setNotSupportsStreaming(); } else if (error == Streaming::Error::OpenFailed) { - _doc->setInappPlaybackFailed(); + _document->setInappPlaybackFailed(); } - if (!_docMedia->canBePlayed()) { + if (!_documentMedia->canBePlayed()) { redisplayContent(); } else { updatePlaybackState(); @@ -2333,10 +2357,10 @@ void OverlayWidget::handleStreamingError(Streaming::Error &&error) { void OverlayWidget::initThemePreview() { using namespace Window::Theme; - Assert(_doc && _doc->isTheme()); + Assert(_document && _document->isTheme()); - const auto bytes = _docMedia->bytes(); - auto &location = _doc->location(); + const auto bytes = _documentMedia->bytes(); + auto &location = _document->location(); if (bytes.isEmpty() && (location.isEmpty() || !location.accessEnable())) { return; @@ -2348,22 +2372,22 @@ void OverlayWidget::initThemePreview() { current.backgroundImage = Background()->createCurrentImage(); current.backgroundTiled = Background()->tile(); - const auto &cloudList = _doc->session().data().cloudThemes().list(); + const auto &cloudList = _document->session().data().cloudThemes().list(); const auto i = ranges::find( cloudList, - _doc->id, + _document->id, &Data::CloudTheme::documentId); const auto cloud = (i != end(cloudList)) ? *i : Data::CloudTheme(); const auto isTrusted = (cloud.documentId != 0); const auto fields = [&] { auto result = _themeCloudData.id ? _themeCloudData : cloud; if (!result.documentId) { - result.documentId = _doc->id; + result.documentId = _document->id; } return result; }(); - const auto path = _doc->location().name(); + const auto path = _document->location().name(); const auto id = _themePreviewId = rand_value<uint64>(); const auto weak = Ui::MakeWeak(this); crl::async([=, data = std::move(current)]() mutable { @@ -2473,10 +2497,10 @@ void OverlayWidget::playbackControlsRotate() { storage.set(_photo, storage.get(_photo) - 90); _rotation = storage.get(_photo); redisplayContent(); - } else if (_doc) { - auto &storage = _doc->owner().mediaRotation(); - storage.set(_doc, storage.get(_doc) - 90); - _rotation = storage.get(_doc); + } else if (_document) { + auto &storage = _document->owner().mediaRotation(); + storage.set(_document, storage.get(_document) - 90); + _rotation = storage.get(_document); if (videoShown()) { applyVideoSize(); update(contentRect()); @@ -2492,7 +2516,7 @@ void OverlayWidget::playbackPauseResume() { _streamed->resumeOnCallEnd = false; if (_streamed->instance.player().failed()) { clearStreaming(); - if (!_docMedia->canBePlayed() || !initStreaming()) { + if (!_documentMedia->canBePlayed() || !initStreaming()) { redisplayContent(); } } else if (_streamed->instance.player().finished() @@ -2511,7 +2535,7 @@ void OverlayWidget::playbackPauseResume() { void OverlayWidget::restartAtSeekPosition(crl::time position) { Expects(_streamed != nullptr); - Expects(_doc != nullptr); + Expects(_document != nullptr); if (videoShown()) { _streamed->instance.saveFrameToCover(); @@ -2522,12 +2546,12 @@ void OverlayWidget::restartAtSeekPosition(crl::time position) { } auto options = Streaming::PlaybackOptions(); options.position = position; - options.audioId = AudioMsgId(_doc, _msgid); + options.audioId = AudioMsgId(_document, _msgid); if (!_streamed->withSound) { options.mode = Streaming::Mode::Video; options.loop = true; } else { - options.speed = _doc->session().settings().videoPlaybackSpeed(); + options.speed = _document->session().settings().videoPlaybackSpeed(); if (_pip) { _pip = nullptr; } @@ -2565,8 +2589,8 @@ void OverlayWidget::playbackControlsVolumeChanged(float64 volume) { Global::SetVideoVolume(volume); updateMixerVideoVolume(); Global::RefVideoVolumeChanged().notify(); - if (_doc) { - _doc->session().saveSettingsDelayed(); + if (_document) { + _document->session().saveSettingsDelayed(); } } @@ -2588,10 +2612,10 @@ void OverlayWidget::playbackControlsVolumeChangeFinished() { void OverlayWidget::playbackControlsSpeedChanged(float64 speed) { DEBUG_LOG(("Media playback speed: change to %1.").arg(speed)); - if (_doc) { + if (_document) { DEBUG_LOG(("Media playback speed: %1 to settings.").arg(speed)); - _doc->session().settings().setVideoPlaybackSpeed(speed); - _doc->session().saveSettingsDelayed(); + _document->session().settings().setVideoPlaybackSpeed(speed); + _document->session().saveSettingsDelayed(); } if (_streamed && !videoIsGifv()) { DEBUG_LOG(("Media playback speed: %1 to _streamed.").arg(speed)); @@ -2600,20 +2624,20 @@ void OverlayWidget::playbackControlsSpeedChanged(float64 speed) { } float64 OverlayWidget::playbackControlsCurrentSpeed() { - const auto result = _doc - ? _doc->session().settings().videoPlaybackSpeed() + const auto result = _document + ? _document->session().settings().videoPlaybackSpeed() : 1.; DEBUG_LOG(("Media playback speed: now %1 (doc %2)." ).arg(result - ).arg(Logs::b(_doc != nullptr))); + ).arg(Logs::b(_document != nullptr))); return result; } void OverlayWidget::switchToPip() { Expects(_streamed != nullptr); - Expects(_doc != nullptr); + Expects(_document != nullptr); - const auto document = _doc; + const auto document = _document; const auto msgId = _msgid; const auto closeAndContinue = [=] { showDocument(document, document->owner().message(msgId), {}, true); @@ -2698,9 +2722,6 @@ void OverlayWidget::updatePlaybackState() { void OverlayWidget::validatePhotoImage(Image *image, bool blurred) { if (!image || !image->loaded()) { - if (!blurred) { - image->load(fileOrigin()); - } return; } else if (!_staticContent.isNull() && (blurred || !_blurred)) { return; @@ -2718,10 +2739,10 @@ void OverlayWidget::validatePhotoImage(Image *image, bool blurred) { } void OverlayWidget::validatePhotoCurrentImage() { - validatePhotoImage(_photo->large(), false); - validatePhotoImage(_photo->thumbnail(), true); - validatePhotoImage(_photo->thumbnailSmall(), true); - validatePhotoImage(_photo->thumbnailInline(), true); + validatePhotoImage(_photoMedia->image(Data::PhotoSize::Large), false); + validatePhotoImage(_photoMedia->image(Data::PhotoSize::Thumbnail), true); + validatePhotoImage(_photoMedia->image(Data::PhotoSize::Small), true); + validatePhotoImage(_photoMedia->thumbnailInline(), true); if (_staticContent.isNull() && _peer && !_msgid @@ -2732,7 +2753,7 @@ void OverlayWidget::validatePhotoCurrentImage() { true); } if (_staticContent.isNull()) { - _photo->loadThumbnailSmall(fileOrigin()); + _photoMedia->wanted(Data::PhotoSize::Small, fileOrigin()); } } @@ -2816,9 +2837,9 @@ void OverlayWidget::paintEvent(QPaintEvent *e) { if (_docIconRect.intersects(r)) { const auto radial = _radial.animating(); const auto radialOpacity = radial ? _radial.opacity() : 0.; - if (!_doc || !_doc->hasThumbnail()) { + if (!_document || !_document->hasThumbnail()) { p.fillRect(_docIconRect, _docIconColor); - if ((!_doc || _docMedia->loaded()) && (!radial || radialOpacity < 1) && _docIcon) { + if ((!_document || _documentMedia->loaded()) && (!radial || radialOpacity < 1) && _docIcon) { _docIcon->paint(p, _docIconRect.x() + (_docIconRect.width() - _docIcon->width()), _docIconRect.y(), width()); p.setPen(st::mediaviewFileExtFg); p.setFont(st::mediaviewFileExtFont); @@ -2826,7 +2847,7 @@ void OverlayWidget::paintEvent(QPaintEvent *e) { p.drawText(_docIconRect.x() + (_docIconRect.width() - _docExtWidth) / 2, _docIconRect.y() + st::mediaviewFileExtTop + st::mediaviewFileExtFont->ascent, _docExt); } } - } else if (const auto thumbnail = _docMedia->thumbnail()) { + } else if (const auto thumbnail = _documentMedia->thumbnail()) { int32 rf(cIntRetinaFactor()); p.drawPixmap(_docIconRect.topLeft(), thumbnail->pix(fileOrigin(), _docThumbw), QRect(_docThumbx * rf, _docThumby * rf, st::mediaviewFileIconSize * rf, st::mediaviewFileIconSize * rf)); } @@ -3029,7 +3050,7 @@ void OverlayWidget::paintTransformedStaticContent(Painter &p) { const auto rect = contentRect(); PainterHighQualityEnabler hq(p); - if ((!_doc || !_docMedia->getStickerLarge()) + if ((!_document || !_documentMedia->getStickerLarge()) && (_staticContent.isNull() || _staticContent.hasAlpha())) { p.fillRect(rect, _transparentBrush); @@ -3060,7 +3081,7 @@ void OverlayWidget::paintRadialLoading( if (!_streamed->instance.waitingShown()) { return; } - } else if (!radial && (!_doc || _docMedia->loaded())) { + } else if (!radial && (!_document || _documentMedia->loaded())) { return; } @@ -3127,11 +3148,11 @@ void OverlayWidget::paintRadialLoadingContent( } else { const auto o = overLevel(OverIcon); paintBg( - _docMedia->loaded() ? radialOpacity : 1., + _documentMedia->loaded() ? radialOpacity : 1., anim::brush(st::msgDateImgBg, st::msgDateImgBgOver, o)); const auto icon = [&]() -> const style::icon * { - if (radial || _doc->loading()) { + if (radial || _document->loading()) { return &st::historyFileThumbCancel; } return &st::historyFileThumbDownload; @@ -3227,7 +3248,7 @@ void OverlayWidget::keyPressEvent(QKeyEvent *e) { } } if (!_menu && e->key() == Qt::Key_Escape) { - if (_doc && _doc->loading() && !_streamed) { + if (_document && _document->loading() && !_streamed) { onDocClick(); } else { close(); @@ -3239,7 +3260,7 @@ void OverlayWidget::keyPressEvent(QKeyEvent *e) { } else if (e->key() == Qt::Key_Enter || e->key() == Qt::Key_Return || e->key() == Qt::Key_Space) { if (_streamed) { playbackPauseResume(); - } else if (_doc && !_doc->loading() && (documentBubbleShown() || !_docMedia->loaded())) { + } else if (_document && !_document->loading() && (documentBubbleShown() || !_documentMedia->loaded())) { onDocClick(); } } else if (e->key() == Qt::Key_Left) { @@ -3485,22 +3506,26 @@ void OverlayWidget::preloadData(int delta) { } } - auto medias = base::flat_set<std::shared_ptr<Data::DocumentMedia>>(); + auto photos = base::flat_set<std::shared_ptr<Data::PhotoMedia>>(); + auto documents = base::flat_set<std::shared_ptr<Data::DocumentMedia>>(); for (auto index = from; index != till; ++index) { auto entity = entityByIndex(index); if (auto photo = base::get_if<not_null<PhotoData*>>(&entity.data)) { - (*photo)->download(fileOrigin()); + const auto [i, ok] = photos.emplace((*photo)->createMediaView()); + (*i)->wanted(Data::PhotoSize::Small, fileOrigin()); + (*i)->wanted(Data::PhotoSize::Large, fileOrigin()); } else if (auto document = base::get_if<not_null<DocumentData*>>( &entity.data)) { - const auto [i, ok] = medias.emplace( + const auto [i, ok] = documents.emplace( (*document)->createMediaView()); - (*document)->loadThumbnail(fileOrigin()); + (*i)->thumbnailWanted(fileOrigin()); if (!(*i)->canBePlayed()) { (*i)->automaticLoad(fileOrigin(), entity.item); } } } - _preloadMedias = std::move(medias); + _preloadPhotos = std::move(photos); + _preloadDocuments = std::move(documents); } void OverlayWidget::mousePressEvent(QMouseEvent *e) { @@ -3694,16 +3719,16 @@ void OverlayWidget::updateOver(QPoint pos) { updateOverState(OverSave); } else if (_rotateNav.contains(pos)) { updateOverState(OverRotate); - } else if (_doc && documentBubbleShown() && _docIconRect.contains(pos)) { + } else if (_document && documentBubbleShown() && _docIconRect.contains(pos)) { updateOverState(OverIcon); } else if (_moreNav.contains(pos)) { updateOverState(OverMore); } else if (_closeNav.contains(pos)) { updateOverState(OverClose); } else if (documentContentShown() && contentRect().contains(pos)) { - if ((_doc->isVideoFile() || _doc->isVideoMessage()) && _streamed) { + if ((_document->isVideoFile() || _document->isVideoMessage()) && _streamed) { updateOverState(OverVideo); - } else if (!_streamed && !_docMedia->loaded()) { + } else if (!_streamed && !_documentMedia->loaded()) { updateOverState(OverIcon); } else if (_over != OverNone) { updateOverState(OverNone); @@ -3763,7 +3788,7 @@ void OverlayWidget::mouseReleaseEvent(QMouseEvent *e) { if (!_themePreviewRect.contains(e->pos())) { close(); } - } else if (!_doc + } else if (!_document || documentContentShown() || !documentBubbleShown() || !_docRect.contains(e->pos())) { @@ -4017,7 +4042,7 @@ void OverlayWidget::findCurrent() { : std::nullopt; _fullCount = _userPhotosData->fullCount(); } else if (_collageData) { - const auto item = _photo ? WebPageCollage::Item(_photo) : _doc; + const auto item = _photo ? WebPageCollage::Item(_photo) : _document; const auto &items = _collageData->items; const auto i = ranges::find(items, item); _index = (i != end(items)) @@ -4034,13 +4059,13 @@ void OverlayWidget::updateHeader() { auto index = _fullIndex ? *_fullIndex : -1; auto count = _fullCount ? *_fullCount : -1; if (index >= 0 && index < count && count > 1) { - if (_doc) { + if (_document) { _headerText = tr::lng_mediaview_file_n_of_amount( tr::now, lt_file, - (_doc->filename().isEmpty() + (_document->filename().isEmpty() ? tr::lng_mediaview_doc_image(tr::now) - : _doc->filename()), + : _document->filename()), lt_n, QString::number(index + 1), lt_amount, @@ -4054,8 +4079,8 @@ void OverlayWidget::updateHeader() { QString::number(count)); } } else { - if (_doc) { - _headerText = _doc->filename().isEmpty() ? tr::lng_mediaview_doc_image(tr::now) : _doc->filename(); + if (_document) { + _headerText = _document->filename().isEmpty() ? tr::lng_mediaview_doc_image(tr::now) : _document->filename(); } else if (_msgid) { _headerText = tr::lng_mediaview_single_photo(tr::now); } else if (_user) { diff --git a/Telegram/SourceFiles/media/view/media_view_overlay_widget.h b/Telegram/SourceFiles/media/view/media_view_overlay_widget.h index 7308e047b..5ccabe462 100644 --- a/Telegram/SourceFiles/media/view/media_view_overlay_widget.h +++ b/Telegram/SourceFiles/media/view/media_view_overlay_widget.h @@ -17,6 +17,11 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "data/data_cloud_themes.h" // Data::CloudTheme. #include "media/view/media_view_playback_controls.h" +namespace Data { +class PhotoMedia; +class DocumentMedia; +} // namespace Data + namespace Ui { class PopupMenu; class LinkButton; @@ -189,6 +194,9 @@ private: void playbackPauseMusic(); void switchToPip(); + void assignMediaPointer(DocumentData *document); + void assignMediaPointer(not_null<PhotoData*> photo); + void updateOver(QPoint mpos); void moveToScreen(bool force = false); bool moveToNext(int delta); @@ -347,9 +355,11 @@ private: QBrush _transparentBrush; PhotoData *_photo = nullptr; - DocumentData *_doc = nullptr; - std::shared_ptr<Data::DocumentMedia> _docMedia; - base::flat_set<std::shared_ptr<Data::DocumentMedia>> _preloadMedias; + DocumentData *_document = nullptr; + std::shared_ptr<Data::PhotoMedia> _photoMedia; + std::shared_ptr<Data::DocumentMedia> _documentMedia; + base::flat_set<std::shared_ptr<Data::PhotoMedia>> _preloadPhotos; + base::flat_set<std::shared_ptr<Data::DocumentMedia>> _preloadDocuments; int _rotation = 0; std::unique_ptr<SharedMedia> _sharedMedia; std::optional<SharedMediaWithLastSlice> _sharedMediaData; diff --git a/Telegram/SourceFiles/overview/overview_layout.cpp b/Telegram/SourceFiles/overview/overview_layout.cpp index 433e26815..439aea9a3 100644 --- a/Telegram/SourceFiles/overview/overview_layout.cpp +++ b/Telegram/SourceFiles/overview/overview_layout.cpp @@ -14,6 +14,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "data/data_media_types.h" #include "data/data_peer.h" #include "data/data_file_origin.h" +#include "data/data_photo_media.h" #include "data/data_document_media.h" #include "styles/style_overview.h" #include "styles/style_history.h" @@ -297,8 +298,8 @@ Photo::Photo( : ItemBase(delegate, parent) , _data(photo) , _link(std::make_shared<PhotoOpenClickHandler>(photo, parent->fullId())) { - if (!_data->thumbnailInline()) { - _data->loadThumbnailSmall(parent->fullId()); + if (_data->inlineThumbnailBytes().isEmpty()) { + _data->load(Data::PhotoSize::Small, parent->fullId()); } } @@ -317,27 +318,25 @@ int32 Photo::resizeGetHeight(int32 width) { } void Photo::paint(Painter &p, const QRect &clip, TextSelection selection, const PaintContext *context) { - bool good = _data->loaded(), selected = (selection == FullSelection); - if (!good) { - _data->thumbnail()->automaticLoad(parent()->fullId(), parent()); - good = _data->thumbnail()->loaded(); - } - if ((good && !_goodLoaded) || _pix.width() != _width * cIntRetinaFactor()) { - _goodLoaded = good; - _pix = QPixmap(); - if (_goodLoaded) { - setPixFrom(_data->loaded() - ? _data->large() - : _data->thumbnail()); - } else if (_data->thumbnailSmall()->loaded()) { - setPixFrom(_data->thumbnailSmall()); - } else if (const auto blurred = _data->thumbnailInline()) { - blurred->load({}); - if (blurred->loaded()) { + const auto selected = (selection == FullSelection); + const auto widthChanged = _pix.width() != _width * cIntRetinaFactor(); + if (!_goodLoaded || widthChanged) { + ensureDataMediaCreated(); + const auto good = _dataMedia->loaded() + || (_dataMedia->image(Data::PhotoSize::Thumbnail) != nullptr); + if ((good && !_goodLoaded) || widthChanged) { + _goodLoaded = good; + _pix = QPixmap(); + if (_goodLoaded) { + setPixFrom(_dataMedia->image(Data::PhotoSize::Large) + ? _dataMedia->image(Data::PhotoSize::Large) + : _dataMedia->image(Data::PhotoSize::Thumbnail)); + } else if (const auto small = _dataMedia->image( + Data::PhotoSize::Small)) { + setPixFrom(small); + } else if (const auto blurred = _dataMedia->thumbnailInline()) { setPixFrom(blurred); } - } else { - _data->loadThumbnailSmall(parent()->fullId()); } } @@ -377,13 +376,30 @@ void Photo::setPixFrom(not_null<Image*> image) { // In case we have inline thumbnail we can unload all images and we still // won't get a blank image in the media viewer when the photo is opened. - if (_data->thumbnailInline() != nullptr) { - _data->unload(); + if (!_data->inlineThumbnailBytes().isEmpty()) { + _dataMedia = nullptr; + delegate()->unregisterHeavyItem(this); } _pix = App::pixmapFromImageInPlace(std::move(img)); } +void Photo::ensureDataMediaCreated() const { + if (_dataMedia) { + return; + } + _dataMedia = _data->createMediaView(); + if (_data->inlineThumbnailBytes().isEmpty()) { + _dataMedia->wanted(Data::PhotoSize::Small, parent()->fullId()); + } + _dataMedia->wanted(Data::PhotoSize::Thumbnail, parent()->fullId()); + delegate()->registerHeavyItem(this); +} + +void Photo::clearHeavyPart() { + _dataMedia = nullptr; +} + TextState Photo::getState( QPoint point, StateRequest request) const { @@ -1460,12 +1476,7 @@ Link::Link( } int32 tw = 0, th = 0; if (_page && _page->photo) { - if (!_page->photo->loaded() - && !_page->photo->thumbnail()->loaded() - && !_page->photo->thumbnailSmall()->loaded()) { - _page->photo->loadThumbnailSmall(parent->fullId()); - } - + _page->photo->load(Data::PhotoSize::Small, parent->fullId()); tw = style::ConvertScale(_page->photo->width()); th = style::ConvertScale(_page->photo->height()); } else if (_page && _page->document && _page->document->hasThumbnail()) { @@ -1609,17 +1620,21 @@ void Link::validateThumbnail() { return; } if (_page && _page->photo) { - if (_page->photo->thumbnail()->loaded()) { - _thumbnail = _page->photo->thumbnail()->pixSingle(parent()->fullId(), _pixw, _pixh, st::linksPhotoSize, st::linksPhotoSize, ImageRoundRadius::Small); - } else if (_page->photo->loaded()) { - _thumbnail = _page->photo->large()->pixSingle(parent()->fullId(), _pixw, _pixh, st::linksPhotoSize, st::linksPhotoSize, ImageRoundRadius::Small); - } else if (_page->photo->thumbnailSmall()->loaded()) { - _thumbnail = _page->photo->thumbnailSmall()->pixSingle(parent()->fullId(), _pixw, _pixh, st::linksPhotoSize, st::linksPhotoSize, ImageRoundRadius::Small); - } else if (const auto blurred = _page->photo->thumbnailInline()) { + using Data::PhotoSize; + ensurePhotoMediaCreated(); + if (const auto thumbnail = _photoMedia->image(PhotoSize::Thumbnail)) { + _thumbnail = thumbnail->pixSingle(parent()->fullId(), _pixw, _pixh, st::linksPhotoSize, st::linksPhotoSize, ImageRoundRadius::Small); + } else if (const auto large = _photoMedia->image(PhotoSize::Large)) { + _thumbnail = large->pixSingle(parent()->fullId(), _pixw, _pixh, st::linksPhotoSize, st::linksPhotoSize, ImageRoundRadius::Small); + } else if (const auto small = _photoMedia->image(PhotoSize::Small)) { + _thumbnail = small->pixSingle(parent()->fullId(), _pixw, _pixh, st::linksPhotoSize, st::linksPhotoSize, ImageRoundRadius::Small); + } else if (const auto blurred = _photoMedia->thumbnailInline()) { _thumbnail = blurred->pixBlurredSingle(parent()->fullId(), _pixw, _pixh, st::linksPhotoSize, st::linksPhotoSize, ImageRoundRadius::Small); } else { return; } + _photoMedia = nullptr; + delegate()->unregisterHeavyItem(this); } else if (_page && _page->document && _page->document->hasThumbnail()) { ensureDocumentMediaCreated(); if (const auto thumbnail = _documentMedia->thumbnail()) { @@ -1664,6 +1679,15 @@ void Link::validateThumbnail() { } } +void Link::ensurePhotoMediaCreated() { + if (_photoMedia) { + return; + } + _photoMedia = _page->photo->createMediaView(); + _photoMedia->wanted(Data::PhotoSize::Small, parent()->fullId()); + delegate()->registerHeavyItem(this); +} + void Link::ensureDocumentMediaCreated() { if (_documentMedia) { return; @@ -1674,6 +1698,7 @@ void Link::ensureDocumentMediaCreated() { } void Link::clearHeavyPart() { + _photoMedia = nullptr; _documentMedia = nullptr; } diff --git a/Telegram/SourceFiles/overview/overview_layout.h b/Telegram/SourceFiles/overview/overview_layout.h index 24bbfb42a..a81d40a18 100644 --- a/Telegram/SourceFiles/overview/overview_layout.h +++ b/Telegram/SourceFiles/overview/overview_layout.h @@ -19,6 +19,7 @@ struct RoundCheckbox; namespace Data { class Media; +class PhotoMedia; class DocumentMedia; } // namespace Data @@ -184,10 +185,14 @@ public: QPoint point, StateRequest request) const override; + void clearHeavyPart() override; + private: + void ensureDataMediaCreated() const; void setPixFrom(not_null<Image*> image); - not_null<PhotoData*> _data; + const not_null<PhotoData*> _data; + mutable std::shared_ptr<Data::PhotoMedia> _dataMedia; ClickHandlerPtr _link; QPixmap _pix; @@ -222,7 +227,7 @@ private: void ensureDataMediaCreated() const; void updateStatusText(); - not_null<DocumentData*> _data; + const not_null<DocumentData*> _data; mutable std::shared_ptr<Data::DocumentMedia> _dataMedia; StatusText _status; @@ -346,6 +351,7 @@ protected: const style::RoundCheckbox &checkboxStyle() const override; private: + void ensurePhotoMediaCreated(); void ensureDocumentMediaCreated(); void validateThumbnail(); @@ -354,6 +360,7 @@ private: QString _title, _letter; int _titlew = 0; WebPageData *_page = nullptr; + std::shared_ptr<Data::PhotoMedia> _photoMedia; std::shared_ptr<Data::DocumentMedia> _documentMedia; int _pixw = 0; int _pixh = 0; diff --git a/Telegram/SourceFiles/storage/file_download.h b/Telegram/SourceFiles/storage/file_download.h index 5eb7f0163..0d7fc1c5d 100644 --- a/Telegram/SourceFiles/storage/file_download.h +++ b/Telegram/SourceFiles/storage/file_download.h @@ -66,29 +66,30 @@ public: [[nodiscard]] Main::Session &session() const; - bool finished() const { + [[nodiscard]] bool finished() const { return _finished; } void finishWithBytes(const QByteArray &data); - bool cancelled() const { + [[nodiscard]] bool cancelled() const { return _cancelled; } - const QByteArray &bytes() const { + [[nodiscard]] const QByteArray &bytes() const { return _data; } - virtual uint64 objId() const { + [[nodiscard]] virtual uint64 objId() const { return 0; } - QByteArray imageFormat(const QSize &shrinkBox = QSize()) const; - QImage imageData(const QSize &shrinkBox = QSize()) const; - QString fileName() const { + [[nodiscard]] QByteArray imageFormat( + const QSize &shrinkBox = QSize()) const; + [[nodiscard]] QImage imageData(const QSize &shrinkBox = QSize()) const; + [[nodiscard]] QString fileName() const { return _filename; } // Used in MainWidget::documentLoadFailed. [[nodiscard]] virtual Data::FileOrigin fileOrigin() const; - float64 currentProgress() const; - virtual int currentOffset() const; - int fullSize() const; + [[nodiscard]] float64 currentProgress() const; + [[nodiscard]] virtual int currentOffset() const; + [[nodiscard]] int fullSize() const; bool setFileName(const QString &filename); // set filename for loaders to cache void permitLoadFromCloud(); @@ -96,10 +97,10 @@ public: void start(); void cancel(); - bool loadingLocal() const { + [[nodiscard]] bool loadingLocal() const { return (_localStatus == LocalStatus::Loading); } - bool autoLoading() const { + [[nodiscard]] bool autoLoading() const { return _autoLoading; } @@ -112,7 +113,7 @@ public: return _updates.events(); } - rpl::lifetime &lifetime() { + [[nodiscard]] rpl::lifetime &lifetime() { return _lifetime; } diff --git a/Telegram/SourceFiles/ui/image/image.cpp b/Telegram/SourceFiles/ui/image/image.cpp index 8e5ed92e6..81c73ffeb 100644 --- a/Telegram/SourceFiles/ui/image/image.cpp +++ b/Telegram/SourceFiles/ui/image/image.cpp @@ -946,14 +946,6 @@ QImage Image::original() const { return _data; } -void Image::automaticLoad( - Data::FileOrigin origin, - const HistoryItem *item) { - if (!loaded()) { - _source->automaticLoad(origin, item); - } -} - void Image::load(Data::FileOrigin origin) { if (!loaded()) { _source->load(origin); diff --git a/Telegram/SourceFiles/ui/image/image.h b/Telegram/SourceFiles/ui/image/image.h index a19fcb0a6..a72a6cced 100644 --- a/Telegram/SourceFiles/ui/image/image.h +++ b/Telegram/SourceFiles/ui/image/image.h @@ -67,11 +67,6 @@ public: virtual QImage takeLoaded() = 0; virtual void unload() = 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; @@ -178,10 +173,6 @@ public: int32 w, int32 h = 0) const; - void automaticLoad(Data::FileOrigin origin, const HistoryItem *item); - void automaticLoadSettingsChanged() { - _source->automaticLoadSettingsChanged(); - } bool loading() const { return _source->loading(); } diff --git a/Telegram/SourceFiles/ui/image/image_location_factory.cpp b/Telegram/SourceFiles/ui/image/image_location_factory.cpp index 9e741691f..64ff8569a 100644 --- a/Telegram/SourceFiles/ui/image/image_location_factory.cpp +++ b/Telegram/SourceFiles/ui/image/image_location_factory.cpp @@ -14,6 +14,65 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL namespace Images { +ImageWithLocation FromPhotoSize( + not_null<Main::Session*> session, + const MTPDphoto &photo, + const MTPPhotoSize &size) { + return size.match([&](const MTPDphotoSize &data) { + return ImageWithLocation{ + .location = ImageLocation( + DownloadLocation{ StorageFileLocation( + photo.vdc_id().v, + session->userId(), + MTP_inputPhotoFileLocation( + photo.vid(), + photo.vaccess_hash(), + photo.vfile_reference(), + data.vtype())) }, + data.vw().v, + data.vh().v), + .bytesCount = data.vsize().v + }; + }, [&](const MTPDphotoCachedSize &data) { + const auto bytes = qba(data.vbytes()); + return ImageWithLocation{ + .location = ImageLocation( + DownloadLocation{ StorageFileLocation( + photo.vdc_id().v, + session->userId(), + MTP_inputPhotoFileLocation( + photo.vid(), + photo.vaccess_hash(), + photo.vfile_reference(), + data.vtype())) }, + data.vw().v, + data.vh().v), + .bytesCount = bytes.size(), + .bytes = bytes + }; + }, [&](const MTPDphotoStrippedSize &data) { + return ImageWithLocation(); + //const auto bytes = ExpandInlineBytes(qba(data.vbytes())); + //return ImageWithLocation{ + // .location = ImageLocation( + // DownloadLocation{ StorageFileLocation( + // photo.vdc_id().v, + // session->userId(), + // MTP_inputPhotoFileLocation( + // photo.vid(), + // photo.vaccess_hash(), + // photo.vfile_reference(), + // data.vtype())) }, + // width, // ??? + // height), // ??? + // .bytesCount = bytes.size(), + // .bytes = bytes + //}; + }, [&](const MTPDphotoSizeEmpty &) { + return ImageWithLocation(); + }); +} + ImageWithLocation FromPhotoSize( not_null<Main::Session*> session, const MTPDdocument &document, diff --git a/Telegram/SourceFiles/ui/image/image_location_factory.h b/Telegram/SourceFiles/ui/image/image_location_factory.h index 666b05660..a3dc8459f 100644 --- a/Telegram/SourceFiles/ui/image/image_location_factory.h +++ b/Telegram/SourceFiles/ui/image/image_location_factory.h @@ -15,6 +15,10 @@ class Session; namespace Images { +[[nodiscard]] ImageWithLocation FromPhotoSize( + not_null<Main::Session*> session, + const MTPDphoto &photo, + const MTPPhotoSize &size); [[nodiscard]] ImageWithLocation FromPhotoSize( not_null<Main::Session*> session, const MTPDdocument &document, diff --git a/Telegram/SourceFiles/ui/image/image_source.cpp b/Telegram/SourceFiles/ui/image/image_source.cpp index 1200f8170..bb73d1aa2 100644 --- a/Telegram/SourceFiles/ui/image/image_source.cpp +++ b/Telegram/SourceFiles/ui/image/image_source.cpp @@ -57,14 +57,6 @@ void ImageSource::unload() { _data = QImage(); } -void ImageSource::automaticLoad( - Data::FileOrigin origin, - const HistoryItem *item) { -} - -void ImageSource::automaticLoadSettingsChanged() { -} - bool ImageSource::loading() { return false; } @@ -186,14 +178,6 @@ void LocalFileSource::unload() { _data = QImage(); } -void LocalFileSource::automaticLoad( - Data::FileOrigin origin, - const HistoryItem *item) { -} - -void LocalFileSource::automaticLoadSettingsChanged() { -} - bool LocalFileSource::loading() { return false; } @@ -344,36 +328,6 @@ bool RemoteSource::loading() { return (_loader != nullptr); } -void RemoteSource::automaticLoad( - Data::FileOrigin origin, - const HistoryItem *item) { - if (!item || cancelled()) { - return; - } - const auto loadFromCloud = Data::AutoDownload::Should( - Auth().settings().autoDownload(), - item->history()->peer, - this); - - if (_loader) { - if (loadFromCloud) { - _loader->permitLoadFromCloud(); - } - } else { - _loader = createLoader( - origin, - loadFromCloud ? LoadFromCloudOrLocal : LoadFromLocalOnly, - true); - } - if (_loader) { - _loader->start(); - } -} - -void RemoteSource::automaticLoadSettingsChanged() { - _cancelled = false; -} - void RemoteSource::load(Data::FileOrigin origin) { if (!_loader) { _loader = createLoader(origin, LoadFromCloudOrLocal, false); @@ -645,33 +599,33 @@ void DelayedStorageSource::performDelayedLoad(Data::FileOrigin origin) { loadLocal(); } } - -void DelayedStorageSource::automaticLoad( - Data::FileOrigin origin, - const HistoryItem *item) { - if (_location.valid()) { - StorageSource::automaticLoad(origin, item); - return; - } else if (_loadCancelled || !item) { - return; - } - const auto loadFromCloud = Data::AutoDownload::Should( - Auth().settings().autoDownload(), - item->history()->peer, - this); - - if (_loadRequested) { - if (loadFromCloud) _loadFromCloud = loadFromCloud; - } else { - _loadFromCloud = loadFromCloud; - _loadRequested = true; - } -} - -void DelayedStorageSource::automaticLoadSettingsChanged() { - if (_loadCancelled) _loadCancelled = false; - StorageSource::automaticLoadSettingsChanged(); -} +// +//void DelayedStorageSource::automaticLoad( +// Data::FileOrigin origin, +// const HistoryItem *item) { +// if (_location.valid()) { +// StorageSource::automaticLoad(origin, item); +// return; +// } else if (_loadCancelled || !item) { +// return; +// } +// const auto loadFromCloud = Data::AutoDownload::Should( +// Auth().settings().autoDownload(), +// item->history()->peer, +// this); +// +// if (_loadRequested) { +// if (loadFromCloud) _loadFromCloud = loadFromCloud; +// } else { +// _loadFromCloud = loadFromCloud; +// _loadRequested = true; +// } +//} +// +//void DelayedStorageSource::automaticLoadSettingsChanged() { +// if (_loadCancelled) _loadCancelled = false; +// StorageSource::automaticLoadSettingsChanged(); +//} void DelayedStorageSource::load(Data::FileOrigin origin) { if (_location.valid()) { diff --git a/Telegram/SourceFiles/ui/image/image_source.h b/Telegram/SourceFiles/ui/image/image_source.h index 44845c7d9..5de31969e 100644 --- a/Telegram/SourceFiles/ui/image/image_source.h +++ b/Telegram/SourceFiles/ui/image/image_source.h @@ -20,11 +20,6 @@ public: QImage takeLoaded() override; void unload() override; - void automaticLoad( - Data::FileOrigin origin, - const HistoryItem *item) override; - void automaticLoadSettingsChanged() override; - bool loading() override; bool displayLoading() override; void cancel() override; @@ -69,11 +64,6 @@ public: QImage takeLoaded() override; void unload() override; - void automaticLoad( - Data::FileOrigin origin, - const HistoryItem *item) override; - void automaticLoadSettingsChanged() override; - bool loading() override; bool displayLoading() override; void cancel() override; @@ -115,11 +105,6 @@ public: QImage takeLoaded() override; void unload() override; - void automaticLoad( - Data::FileOrigin origin, - const HistoryItem *item) override; - void automaticLoadSettingsChanged() override; - bool loading() override; bool displayLoading() override; void cancel() override; @@ -256,11 +241,6 @@ public: 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.valid() ? StorageSource::loading() diff --git a/Telegram/SourceFiles/window/window_media_preview.cpp b/Telegram/SourceFiles/window/window_media_preview.cpp index f0693edef..3e2197558 100644 --- a/Telegram/SourceFiles/window/window_media_preview.cpp +++ b/Telegram/SourceFiles/window/window_media_preview.cpp @@ -8,6 +8,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "window/window_media_preview.h" #include "data/data_photo.h" +#include "data/data_photo_media.h" #include "data/data_document.h" #include "data/data_document_media.h" #include "ui/image/image.h" @@ -114,6 +115,7 @@ void MediaPreviewWidget::showPreview( startShow(); _origin = origin; _photo = nullptr; + _photoMedia = nullptr; _document = document; _documentMedia = _document->createMediaView(); _documentMedia->thumbnailWanted(_origin); @@ -128,9 +130,10 @@ void MediaPreviewWidget::showPreview( not_null<PhotoData*> photo) { startShow(); _origin = origin; - _photo = photo; _document = nullptr; _documentMedia = nullptr; + _photo = photo; + _photoMedia = _photo->createMediaView(); fillEmojiString(); resetGifAndCache(); } @@ -159,6 +162,7 @@ void MediaPreviewWidget::hidePreview() { _hiding = true; _a_shown.start([=] { update(); }, 1., 0., st::stickerPreviewDuration); _photo = nullptr; + _photoMedia = nullptr; _document = nullptr; _documentMedia = nullptr; resetGifAndCache(); @@ -296,30 +300,31 @@ QPixmap MediaPreviewWidget::currentImage() const { } } else if (_photo) { if (_cacheStatus != CacheLoaded) { - if (_photo->loaded()) { + if (_photoMedia->loaded()) { QSize s = currentDimensions(); - _cache = _photo->large()->pix(_origin, s.width(), s.height()); + _cache = _photoMedia->image(Data::PhotoSize::Large)->pix(_origin, s.width(), s.height()); _cacheStatus = CacheLoaded; } else { _photo->load(_origin); if (_cacheStatus != CacheThumbLoaded) { QSize s = currentDimensions(); - if (_photo->thumbnail()->loaded()) { - _cache = _photo->thumbnail()->pixBlurred(_origin, s.width(), s.height()); + if (const auto thumbnail = _photoMedia->image( + Data::PhotoSize::Thumbnail)) { + _cache = thumbnail->pixBlurred(_origin, s.width(), s.height()); _cacheStatus = CacheThumbLoaded; - } else if (_photo->thumbnailSmall()->loaded()) { - _cache = _photo->thumbnailSmall()->pixBlurred(_origin, s.width(), s.height()); + } else if (const auto small = _photoMedia->image( + Data::PhotoSize::Small)) { + _cache = small->pixBlurred(_origin, s.width(), s.height()); _cacheStatus = CacheThumbLoaded; - } else if (const auto blurred = _photo->thumbnailInline()) { + } else if (const auto blurred = _photoMedia->thumbnailInline()) { _cache = blurred->pixBlurred(_origin, s.width(), s.height()); _cacheStatus = CacheThumbLoaded; } else { - _photo->thumbnailSmall()->load(_origin); + _photoMedia->wanted(Data::PhotoSize::Small, _origin); } } } } - } return _cache; } diff --git a/Telegram/SourceFiles/window/window_media_preview.h b/Telegram/SourceFiles/window/window_media_preview.h index ce334a505..a7d1283fd 100644 --- a/Telegram/SourceFiles/window/window_media_preview.h +++ b/Telegram/SourceFiles/window/window_media_preview.h @@ -13,6 +13,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "ui/rp_widget.h" namespace Data { +class PhotoMedia; class DocumentMedia; } // namespace Data @@ -60,9 +61,10 @@ private: Ui::Animations::Simple _a_shown; bool _hiding = false; Data::FileOrigin _origin; - DocumentData *_document = nullptr; - std::shared_ptr<Data::DocumentMedia> _documentMedia; PhotoData *_photo = nullptr; + DocumentData *_document = nullptr; + std::shared_ptr<Data::PhotoMedia> _photoMedia; + std::shared_ptr<Data::DocumentMedia> _documentMedia; Media::Clip::ReaderPointer _gif, _gifThumbnail; crl::time _gifLastPosition = 0; std::unique_ptr<Lottie::SinglePlayer> _lottie;