diff --git a/Telegram/SourceFiles/apiwrap.cpp b/Telegram/SourceFiles/apiwrap.cpp index 588eab821..361d5756b 100644 --- a/Telegram/SourceFiles/apiwrap.cpp +++ b/Telegram/SourceFiles/apiwrap.cpp @@ -894,7 +894,7 @@ void ApiWrap::requestWallPaper( data.vaccess_hash.v, data.vflags.v, qs(data.vslug), - document->thumb, + document->thumbnail(), document }); } diff --git a/Telegram/SourceFiles/boxes/background_box.cpp b/Telegram/SourceFiles/boxes/background_box.cpp index 069783434..c239cfac9 100644 --- a/Telegram/SourceFiles/boxes/background_box.cpp +++ b/Telegram/SourceFiles/boxes/background_box.cpp @@ -95,7 +95,7 @@ QImage PrepareScaledFromFull( size); } -QPixmap PrepareScaledFromThumb(ImagePtr thumb) { +QPixmap PrepareScaledFromThumb(not_null thumb) { return thumb->loaded() ? App::pixmapFromImageInPlace(PrepareScaledFromFull( thumb->original(), @@ -315,7 +315,7 @@ void BackgroundPreviewBox::prepare() { _scaled = PrepareScaledFromThumb(_paper.thumb); checkLoadedDocument(); - if (_paper.thumb && !_paper.thumb->loaded()) { + if (!_paper.thumb->loaded()) { _paper.thumb->loadEvenCancelled(Data::FileOriginWallpaper( _paper.id, _paper.accessHash)); diff --git a/Telegram/SourceFiles/boxes/edit_caption_box.cpp b/Telegram/SourceFiles/boxes/edit_caption_box.cpp index c673854f2..6f025648e 100644 --- a/Telegram/SourceFiles/boxes/edit_caption_box.cpp +++ b/Telegram/SourceFiles/boxes/edit_caption_box.cpp @@ -42,17 +42,17 @@ EditCaptionBox::EditCaptionBox( Expects(item->media()->allowsEditCaption()); QSize dimensions; - ImagePtr image; + auto image = (Image*)nullptr; DocumentData *doc = nullptr; const auto media = item->media(); if (const auto photo = media->photo()) { _photo = true; - dimensions = QSize(photo->full->width(), photo->full->height()); - image = photo->full; + dimensions = QSize(photo->width(), photo->height()); + image = photo->large(); } else if (const auto document = media->document()) { dimensions = document->dimensions; - image = document->thumb; + image = document->thumbnail(); if (document->isAnimation()) { _animated = true; } else if (document->isVideoFile()) { @@ -68,8 +68,8 @@ EditCaptionBox::EditCaptionBox( ConvertEntitiesToTextTags(original.entities) }; - if (!_animated && (dimensions.isEmpty() || doc || image->isNull())) { - if (image->isNull()) { + if (!_animated && (dimensions.isEmpty() || doc || !image)) { + if (!image) { _thumbw = 0; } else { int32 tw = image->width(), th = image->height(); diff --git a/Telegram/SourceFiles/boxes/edit_caption_box.h b/Telegram/SourceFiles/boxes/edit_caption_box.h index 7f9c4788a..8f8d47bdd 100644 --- a/Telegram/SourceFiles/boxes/edit_caption_box.h +++ b/Telegram/SourceFiles/boxes/edit_caption_box.h @@ -63,7 +63,7 @@ private: not_null _controller; FullMsgId _msgId; - ImagePtr _thumbnailImage; + Image *_thumbnailImage = nullptr; bool _thumbnailImageLoaded = false; Fn _refreshThumbnail; bool _animated = false; diff --git a/Telegram/SourceFiles/boxes/sticker_set_box.cpp b/Telegram/SourceFiles/boxes/sticker_set_box.cpp index efb8e1e5b..701bd8918 100644 --- a/Telegram/SourceFiles/boxes/sticker_set_box.cpp +++ b/Telegram/SourceFiles/boxes/sticker_set_box.cpp @@ -472,7 +472,7 @@ void StickerSetBox::Inner::paintEvent(QPaintEvent *e) { p.setOpacity(1); } - doc->checkStickerThumb(); + doc->checkStickerSmall(); float64 coef = qMin((st::stickersSize.width() - st::buttonRadius * 2) / float64(doc->dimensions.width()), (st::stickersSize.height() - st::buttonRadius * 2) / float64(doc->dimensions.height())); if (coef > 1) coef = 1; @@ -480,7 +480,7 @@ void StickerSetBox::Inner::paintEvent(QPaintEvent *e) { if (w < 1) w = 1; if (h < 1) h = 1; QPoint ppos = pos + QPoint((st::stickersSize.width() - w) / 2, (st::stickersSize.height() - h) / 2); - if (const auto image = doc->getStickerThumb()) { + if (const auto image = doc->getStickerSmall()) { p.drawPixmapLeft( ppos, width(), diff --git a/Telegram/SourceFiles/boxes/stickers_box.cpp b/Telegram/SourceFiles/boxes/stickers_box.cpp index 366c5eb28..b7c0a6b58 100644 --- a/Telegram/SourceFiles/boxes/stickers_box.cpp +++ b/Telegram/SourceFiles/boxes/stickers_box.cpp @@ -804,9 +804,11 @@ void StickersBox::Inner::paintRow(Painter &p, Row *set, int index, TimeMs ms) { const auto origin = Data::FileOriginStickerSet( set->id, set->accessHash); - set->sticker->thumb->load(origin); - auto pix = set->sticker->thumb->pix(origin, set->pixw, set->pixh); - p.drawPixmapLeft(stickerx + (st::contactsPhotoSize - set->pixw) / 2, st::contactsPadding.top() + (st::contactsPhotoSize - set->pixh) / 2, width(), pix); + if (const auto thumb = set->sticker->thumbnail()) { + thumb->load(origin); + auto pix = thumb->pix(origin, set->pixw, set->pixh); + p.drawPixmapLeft(stickerx + (st::contactsPhotoSize - set->pixw) / 2, st::contactsPadding.top() + (st::contactsPhotoSize - set->pixh) / 2, width(), pix); + } } int namex = stickerx + st::contactsPhotoSize + st::contactsPadding.left(); @@ -1575,8 +1577,11 @@ void StickersBox::Inner::fillSetCover(const Stickers::Set &set, DocumentData **o } auto sticker = *outSticker = set.stickers.front(); - auto pixw = sticker->thumb->width(); - auto pixh = sticker->thumb->height(); + const auto size = sticker->thumbnail() + ? sticker->thumbnail()->size() + : QSize(1, 1); + auto pixw = size.width(); + auto pixh = size.height(); if (pixw > st::contactsPhotoSize) { if (pixw > pixh) { pixh = (pixh * st::contactsPhotoSize) / pixw; @@ -1734,7 +1739,10 @@ void StickersBox::Inner::readVisibleSets() { if (i * _rowHeight < itemsVisibleTop || (i + 1) * _rowHeight > itemsVisibleBottom) { continue; } - if (!_rows[i]->sticker || _rows[i]->sticker->thumb->loaded() || _rows[i]->sticker->loaded()) { + if (!_rows[i]->sticker + || !_rows[i]->sticker->hasThumbnail() + || _rows[i]->sticker->thumbnail()->loaded() + || _rows[i]->sticker->loaded()) { Auth().api().readFeaturedSetDelayed(_rows[i]->id); } } diff --git a/Telegram/SourceFiles/calls/calls_panel.cpp b/Telegram/SourceFiles/calls/calls_panel.cpp index a247e1d0d..315483c8a 100644 --- a/Telegram/SourceFiles/calls/calls_panel.cpp +++ b/Telegram/SourceFiles/calls/calls_panel.cpp @@ -503,34 +503,39 @@ void Panel::processUserPhoto() { _user->loadUserpic(true); } const auto photo = _user->userpicPhotoId() - ? Auth().data().photo(_user->userpicPhotoId()).get() + ? _user->owner().photo(_user->userpicPhotoId()).get() : nullptr; if (isGoodUserPhoto(photo)) { - photo->full->load(_user->userpicPhotoOrigin(), true); + photo->large()->load(_user->userpicPhotoOrigin(), true); } else if (_user->userpicPhotoUnknown() || (photo && !photo->date)) { - Auth().api().requestFullPeer(_user); + _user->session().api().requestFullPeer(_user); } refreshUserPhoto(); } void Panel::refreshUserPhoto() { const auto photo = _user->userpicPhotoId() - ? Auth().data().photo(_user->userpicPhotoId()).get() + ? _user->owner().photo(_user->userpicPhotoId()).get() : nullptr; const auto isNewPhoto = [&](not_null photo) { - return photo->full->loaded() + return photo->large()->loaded() && (photo->id != _userPhotoId || !_userPhotoFull); }; if (isGoodUserPhoto(photo) && isNewPhoto(photo)) { _userPhotoId = photo->id; _userPhotoFull = true; - createUserpicCache(photo->full, _user->userpicPhotoOrigin()); + createUserpicCache( + photo->isNull() ? nullptr : photo->large().get(), + _user->userpicPhotoOrigin()); } else if (_userPhoto.isNull()) { - createUserpicCache(_user->currentUserpic(), _user->userpicOrigin()); + const auto userpic = _user->currentUserpic(); + createUserpicCache( + userpic ? userpic.get() : nullptr, + _user->userpicOrigin()); } } -void Panel::createUserpicCache(ImagePtr image, Data::FileOrigin origin) { +void Panel::createUserpicCache(Image *image, Data::FileOrigin origin) { auto size = st::callWidth * cIntRetinaFactor(); auto options = _useTransparency ? (Images::Option::RoundedLarge | Images::Option::RoundedTopLeft | Images::Option::RoundedTopRight | Images::Option::Smooth) : Images::Option::None; if (image) { @@ -570,14 +575,14 @@ void Panel::createUserpicCache(ImagePtr image, Data::FileOrigin origin) { } bool Panel::isGoodUserPhoto(PhotoData *photo) { - if (!photo || !photo->date) { + if (!photo || photo->isNull()) { return false; } - auto badAspect = [](int a, int b) { + const auto badAspect = [](int a, int b) { return a > 10 * b; }; - auto width = photo->full->width(); - auto height = photo->full->height(); + const auto width = photo->width(); + const auto height = photo->height(); return !badAspect(width, height) && !badAspect(height, width); } diff --git a/Telegram/SourceFiles/calls/calls_panel.h b/Telegram/SourceFiles/calls/calls_panel.h index 16b776dd6..d7eef4d22 100644 --- a/Telegram/SourceFiles/calls/calls_panel.h +++ b/Telegram/SourceFiles/calls/calls_panel.h @@ -92,7 +92,7 @@ private: void processUserPhoto(); void refreshUserPhoto(); bool isGoodUserPhoto(PhotoData *photo); - void createUserpicCache(ImagePtr image, Data::FileOrigin origin); + void createUserpicCache(Image *image, Data::FileOrigin origin); QRect signalBarsRect() const; void paintSignalBarsBg(Painter &p); diff --git a/Telegram/SourceFiles/chat_helpers/field_autocomplete.cpp b/Telegram/SourceFiles/chat_helpers/field_autocomplete.cpp index 8c3fb451b..af5b8e1fe 100644 --- a/Telegram/SourceFiles/chat_helpers/field_autocomplete.cpp +++ b/Telegram/SourceFiles/chat_helpers/field_autocomplete.cpp @@ -561,7 +561,7 @@ void FieldAutocompleteInner::paintEvent(QPaintEvent *e) { App::roundRect(p, QRect(tl, st::stickerPanSize), st::emojiPanHover, StickerHoverCorners); } - document->checkStickerThumb(); + document->checkStickerSmall(); float64 coef = qMin((st::stickerPanSize.width() - st::buttonRadius * 2) / float64(document->dimensions.width()), (st::stickerPanSize.height() - st::buttonRadius * 2) / float64(document->dimensions.height())); if (coef > 1) coef = 1; @@ -569,7 +569,7 @@ void FieldAutocompleteInner::paintEvent(QPaintEvent *e) { if (w < 1) w = 1; if (h < 1) h = 1; QPoint ppos = pos + QPoint((st::stickerPanSize.width() - w) / 2, (st::stickerPanSize.height() - h) / 2); - if (const auto image = document->getStickerThumb()) { + if (const auto image = document->getStickerSmall()) { p.drawPixmapLeft(ppos, width(), image->pix(document->stickerSetOrigin(), w, h)); } } diff --git a/Telegram/SourceFiles/chat_helpers/gifs_list_widget.cpp b/Telegram/SourceFiles/chat_helpers/gifs_list_widget.cpp index 64ae3e35b..c93d552c0 100644 --- a/Telegram/SourceFiles/chat_helpers/gifs_list_widget.cpp +++ b/Telegram/SourceFiles/chat_helpers/gifs_list_widget.cpp @@ -352,11 +352,10 @@ void GifsListWidget::selectInlineResult(int row, int column) { auto item = _rows[row].items[column]; if (const auto photo = item->getPhoto()) { - if (photo->medium->loaded() || photo->thumb->loaded()) { + if (photo->thumbnail()->loaded()) { _photoChosen.fire_copy(photo); - } else if (!photo->medium->loading()) { - photo->thumb->loadEvenCancelled(Data::FileOrigin()); - photo->medium->loadEvenCancelled(Data::FileOrigin()); + } else if (!photo->thumbnail()->loading()) { + photo->thumbnail()->loadEvenCancelled(Data::FileOrigin()); } } else if (const auto document = item->getDocument()) { if (document->loaded()) { diff --git a/Telegram/SourceFiles/chat_helpers/stickers_list_widget.cpp b/Telegram/SourceFiles/chat_helpers/stickers_list_widget.cpp index 8afcca20c..76c9f29da 100644 --- a/Telegram/SourceFiles/chat_helpers/stickers_list_widget.cpp +++ b/Telegram/SourceFiles/chat_helpers/stickers_list_widget.cpp @@ -259,8 +259,8 @@ void StickersListWidget::Footer::enumerateVisibleIcons(Callback callback) { void StickersListWidget::Footer::preloadImages() { enumerateVisibleIcons([](const StickerIcon &icon, int x) { - if (auto sticker = icon.sticker) { - sticker->thumb->load(sticker->stickerSetOrigin()); + if (const auto sticker = icon.sticker) { + sticker->loadThumbnail(sticker->stickerSetOrigin()); } }); } @@ -631,10 +631,12 @@ void StickersListWidget::Footer::paintSetIcon( int x) const { if (icon.sticker) { const auto origin = icon.sticker->stickerSetOrigin(); - icon.sticker->thumb->load(origin); - auto pix = icon.sticker->thumb->pix(origin, icon.pixw, icon.pixh); + if (const auto thumb = icon.sticker->thumbnail()) { + thumb->load(origin); + auto pix = thumb->pix(origin, icon.pixw, icon.pixh); - p.drawPixmapLeft(x + (st::stickerIconWidth - icon.pixw) / 2, _iconsTop + (st::emojiFooterHeight - icon.pixh) / 2, width(), pix); + p.drawPixmapLeft(x + (st::stickerIconWidth - icon.pixw) / 2, _iconsTop + (st::emojiFooterHeight - icon.pixh) / 2, width(), pix); + } } else if (icon.megagroup) { icon.megagroup->paintUserpicLeft(p, x + (st::stickerIconWidth - st::stickerGroupCategorySize) / 2, _iconsTop + (st::emojiFooterHeight - st::stickerGroupCategorySize) / 2, width(), st::stickerGroupCategorySize); } else { @@ -753,7 +755,9 @@ void StickersListWidget::readVisibleSets() { int count = qMin(set.pack.size(), _columnCount); int loaded = 0; for (int j = 0; j < count; ++j) { - if (set.pack[j]->thumb->loaded() || set.pack[j]->loaded()) { + if (!set.pack[j]->hasThumbnail() + || set.pack[j]->thumbnail()->loaded() + || set.pack[j]->loaded()) { ++loaded; } } @@ -1339,14 +1343,14 @@ void StickersListWidget::paintSticker(Painter &p, Set &set, int y, int index, bo App::roundRect(p, QRect(tl, _singleSize), st::emojiPanHover, StickerHoverCorners); } - document->checkStickerThumb(); + document->checkStickerSmall(); auto coef = qMin((_singleSize.width() - st::buttonRadius * 2) / float64(document->dimensions.width()), (_singleSize.height() - st::buttonRadius * 2) / float64(document->dimensions.height())); if (coef > 1) coef = 1; auto w = qMax(qRound(coef * document->dimensions.width()), 1); auto h = qMax(qRound(coef * document->dimensions.height()), 1); auto ppos = pos + QPoint((_singleSize.width() - w) / 2, (_singleSize.height() - h) / 2); - if (const auto image = document->getStickerThumb()) { + if (const auto image = document->getStickerSmall()) { if (image->loaded()) { p.drawPixmapLeft( ppos, @@ -1786,7 +1790,7 @@ void StickersListWidget::preloadImages() { const auto document = sets[i].pack[j]; if (!document || !document->sticker()) continue; - document->checkStickerThumb(); + document->checkStickerSmall(); } if (k > _columnCount * (_columnCount + 1)) break; } @@ -2047,7 +2051,10 @@ void StickersListWidget::fillIcons(QList &icons) { } auto s = _mySets[i].pack[0]; auto availw = st::stickerIconWidth - 2 * st::stickerIconPadding, availh = st::emojiFooterHeight - 2 * st::stickerIconPadding; - auto thumbw = s->thumb->width(), thumbh = s->thumb->height(), pixw = 1, pixh = 1; + const auto size = s->hasThumbnail() + ? s->thumbnail()->size() + : QSize(); + auto thumbw = size.width(), thumbh = size.height(), pixw = 1, pixh = 1; if (availw * thumbh > availh * thumbw) { pixh = availh; pixw = (pixh * thumbw) / thumbh; diff --git a/Telegram/SourceFiles/data/data_document.cpp b/Telegram/SourceFiles/data/data_document.cpp index 867ff2f63..5bcc30a54 100644 --- a/Telegram/SourceFiles/data/data_document.cpp +++ b/Telegram/SourceFiles/data/data_document.cpp @@ -25,7 +25,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "boxes/confirm_box.h" #include "ui/image/image.h" #include "ui/image/image_source.h" -#include "auth_session.h" #include "mainwindow.h" #include "core/application.h" @@ -318,7 +317,7 @@ void DocumentOpenClickHandler::Open( auto audio = AudioMsgId(data, msgId); Media::Player::mixer()->play(audio); Media::Player::Updated().notify(audio); - data->session()->data().markMediaRead(data); + data->owner().markMediaRead(data); } } else if (playMusic) { auto state = Media::Player::mixer()->currentState(AudioMsgId::Type::Song); @@ -345,24 +344,24 @@ void DocumentOpenClickHandler::Open( File::Launch(filepath); } } - data->session()->data().markMediaRead(data); + data->owner().markMediaRead(data); } else if (data->isVoiceMessage() || data->isAudioFile() || data->isVideoFile()) { const auto filepath = location.name(); if (Data::IsValidMediaFile(filepath)) { File::Launch(filepath); } - data->session()->data().markMediaRead(data); + data->owner().markMediaRead(data); } else if (data->size < App::kImageSizeLimit) { if (!data->data().isEmpty() && playAnimation) { if (action == ActionOnLoadPlayInline && context) { - data->session()->data().requestAnimationPlayInline(context); + data->owner().requestAnimationPlayInline(context); } else { Core::App().showDocument(data, context); } } else if (location.accessEnable()) { if (playAnimation || QImageReader(location.name()).canRead()) { if (playAnimation && action == ActionOnLoadPlayInline && context) { - data->session()->data().requestAnimationPlayInline(context); + data->owner().requestAnimationPlayInline(context); } else { Core::App().showDocument(data, context); } @@ -462,13 +461,17 @@ VoiceData::~VoiceData() { } } -DocumentData::DocumentData(DocumentId id, not_null session) +DocumentData::DocumentData(not_null owner, DocumentId id) : id(id) -, _session(session) { +, _owner(owner) { } -not_null DocumentData::session() const { - return _session; +Data::Session &DocumentData::owner() const { + return *_owner; +} + +AuthSession &DocumentData::session() const { + return _owner->session(); } void DocumentData::setattributes(const QVector &attributes) { @@ -581,7 +584,7 @@ bool DocumentData::checkWallPaperProperties() { return true; } if (type != FileDocument - || !thumb + || !_thumbnail || !dimensions.width() || !dimensions.height() || dimensions.width() > Storage::kMaxWallPaperDimension @@ -593,10 +596,43 @@ bool DocumentData::checkWallPaperProperties() { return true; } +void DocumentData::updateThumbnails( + ImagePtr thumbnailInline, + ImagePtr thumbnail) { + if (thumbnailInline && !_thumbnailInline) { + _thumbnailInline = thumbnailInline; + } + if (thumbnail + && (!_thumbnail + || (sticker() + && (_thumbnail->width() < thumbnail->width() + || _thumbnail->height() < thumbnail->height())))) { + _thumbnail = thumbnail; + } +} + bool DocumentData::isWallPaper() const { return (type == WallPaperDocument); } +bool DocumentData::hasThumbnail() const { + return !_thumbnail->isNull(); +} + +Image *DocumentData::thumbnailInline() const { + return _thumbnailInline ? _thumbnailInline.get() : nullptr; +} + +Image *DocumentData::thumbnail() const { + return _thumbnail ? _thumbnail.get() : nullptr; +} + +void DocumentData::loadThumbnail(Data::FileOrigin origin) { + if (_thumbnail && !_thumbnail->loaded()) { + _thumbnail->load(origin); + } +} + Storage::Cache::Key DocumentData::goodThumbnailCacheKey() const { return Data::DocumentThumbCacheKey(_dc, id); } @@ -645,14 +681,19 @@ bool DocumentData::saveToCache() const { void DocumentData::unload() { // Forget thumb only when image cache limit exceeds. - //thumb->unload(); + // + // Also, you can't unload() images that you don't own + // from the destructor, because they're already destroyed. + // + //_thumbnailInline->unload(); + //_thumbnail->unload(); if (sticker()) { if (sticker()->image) { ActiveCache().decrement(ComputeUsage(sticker())); sticker()->image = nullptr; } } - _replyPreview = nullptr; + _replyPreview.clear(); if (!_data.isEmpty()) { ActiveCache().decrement(_data.size()); _data.clear(); @@ -737,7 +778,7 @@ void DocumentData::performActionOnLoad() { } } else if (Media::Player::IsStopped(state.state)) { Media::Player::mixer()->play(AudioMsgId(this, _actionOnLoadMsgId)); - _session->data().markMediaRead(this); + _owner->markMediaRead(this); } } } else if (playMusic) { @@ -758,7 +799,7 @@ void DocumentData::performActionOnLoad() { } else if (playAnimation) { if (loaded()) { if (_actionOnLoad == ActionOnLoadPlayInline && item) { - _session->data().requestAnimationPlayInline(item); + _owner->requestAnimationPlayInline(item); } else { Core::App().showDocument(this, item); } @@ -773,7 +814,7 @@ void DocumentData::performActionOnLoad() { if (Data::IsValidMediaFile(already)) { File::Launch(already); } - _session->data().markMediaRead(this); + _owner->markMediaRead(this); } else if (loc.accessEnable()) { if (showImage && QImageReader(loc.name()).canRead()) { Core::App().showDocument(this, item); @@ -810,14 +851,14 @@ bool DocumentData::loaded(FilePathResolveType type) const { _loader->imageData())); ActiveCache().increment(ComputeUsage(that->sticker())); } - if (!that->_data.isEmpty() || that->getStickerImage()) { + if (!that->_data.isEmpty() || that->getStickerLarge()) { ActiveCache().up(that); } that->refreshGoodThumbnail(); destroyLoader(); } - _session->data().notifyDocumentLayoutChanged(this); + _owner->notifyDocumentLayoutChanged(this); } return !data().isEmpty() || !filepath(type).isEmpty(); } @@ -962,7 +1003,7 @@ void DocumentData::save( if (loading()) { _loader->start(); } - _session->data().notifyDocumentLayoutChanged(this); + _owner->notifyDocumentLayoutChanged(this); } void DocumentData::cancel() { @@ -971,7 +1012,7 @@ void DocumentData::cancel() { } destroyLoader(CancelledMtpFileLoader); - _session->data().notifyDocumentLayoutChanged(this); + _owner->notifyDocumentLayoutChanged(this); App::main()->documentLoadProgress(this); _actionOnLoad = ActionOnLoadNone; @@ -1085,7 +1126,7 @@ bool DocumentData::isStickerSetInstalled() const { Expects(sticker() != nullptr); const auto &set = sticker()->set; - const auto &sets = _session->data().stickerSets(); + const auto &sets = _owner->stickerSets(); switch (set.type()) { case mtpc_inputStickerSetID: { auto it = sets.constFind(set.c_inputStickerSetID().vid.v); @@ -1107,25 +1148,30 @@ bool DocumentData::isStickerSetInstalled() const { } Image *DocumentData::getReplyPreview(Data::FileOrigin origin) { - if (!_replyPreview->isNull() && !thumb->isNull()) { - if (thumb->loaded()) { - int w = thumb->width(), h = thumb->height(); - if (w <= 0) w = 1; - if (h <= 0) h = 1; - auto thumbSize = (w > h) ? QSize(w * st::msgReplyBarSize.height() / h, st::msgReplyBarSize.height()) : QSize(st::msgReplyBarSize.height(), h * st::msgReplyBarSize.height() / w); - thumbSize *= cIntRetinaFactor(); - auto options = Images::Option::Smooth | (isVideoMessage() ? Images::Option::Circled : Images::Option::None) | Images::Option::TransparentBackground; - auto outerSize = st::msgReplyBarSize.height(); - auto image = thumb->pixNoCache(origin, thumbSize.width(), thumbSize.height(), options, outerSize, outerSize); - _replyPreview = std::make_unique( - std::make_unique( - image.toImage(), - "PNG")); - } else { - thumb->load(origin); + if (!_thumbnail) { + return nullptr; + } else if (_replyPreview + && (_replyPreview.good() || !_thumbnail->loaded())) { + return _replyPreview.image(); + } + const auto option = isVideoMessage() + ? Images::Option::Circled + : Images::Option::None; + if (_thumbnail->loaded()) { + _replyPreview.prepare( + _thumbnail.get(), + origin, + option); + } else { + _thumbnail->load(origin); + if (_thumbnailInline) { + _replyPreview.prepare( + _thumbnailInline.get(), + origin, + option | Images::Option::Blurred); } } - return _replyPreview.get(); + return _replyPreview.image(); } StickerData *DocumentData::sticker() const { @@ -1134,7 +1180,7 @@ StickerData *DocumentData::sticker() const { : nullptr; } -void DocumentData::checkSticker() { +void DocumentData::checkStickerLarge() { const auto data = sticker(); if (!data) return; @@ -1164,25 +1210,25 @@ void DocumentData::checkSticker() { } } -void DocumentData::checkStickerThumb() { - if (hasGoodStickerThumb()) { - thumb->load(stickerSetOrigin()); +void DocumentData::checkStickerSmall() { + if (thumbnailEnoughForSticker()) { + _thumbnail->load(stickerSetOrigin()); } else { - checkSticker(); + checkStickerLarge(); } } -Image *DocumentData::getStickerImage() { - checkSticker(); +Image *DocumentData::getStickerLarge() { + checkStickerLarge(); if (const auto data = sticker()) { return data->image.get(); } return nullptr; } -Image *DocumentData::getStickerThumb() { - if (hasGoodStickerThumb()) { - return thumb->isNull() ? nullptr : thumb.get(); +Image *DocumentData::getStickerSmall() { + if (thumbnailEnoughForSticker()) { + return _thumbnail->isNull() ? nullptr : _thumbnail.get(); } else if (const auto data = sticker()) { return data->image.get(); } @@ -1236,8 +1282,8 @@ bool DocumentData::hasWebLocation() const { return _urlLocation.dc() != 0 && _urlLocation.accessHash() != 0; } -bool DocumentData::isValid() const { - return hasRemoteLocation() || hasWebLocation() || !_url.isEmpty(); +bool DocumentData::isNull() const { + return !hasRemoteLocation() && !hasWebLocation() && _url.isEmpty(); } MTPInputDocument DocumentData::mtpInput() const { @@ -1260,9 +1306,9 @@ void DocumentData::refreshFileReference(const QByteArray &value) { void DocumentData::refreshStickerThumbFileReference() { if (const auto data = sticker()) { - if (thumb->loading()) { + if (_thumbnail->loading()) { data->loc.refreshFileReference( - thumb->location().fileReference()); + _thumbnail->location().fileReference()); } } } @@ -1407,9 +1453,9 @@ void DocumentData::recountIsImage() { && fileIsImage(filename(), mimeString()); } -bool DocumentData::hasGoodStickerThumb() const { - return !thumb->isNull() - && ((thumb->width() >= 128) || (thumb->height() >= 128)); +bool DocumentData::thumbnailEnoughForSticker() const { + return !_thumbnail->isNull() + && ((_thumbnail->width() >= 128) || (_thumbnail->height() >= 128)); } void DocumentData::setRemoteLocation( @@ -1420,7 +1466,7 @@ void DocumentData::setRemoteLocation( if (_dc != dc || _access != access) { _dc = dc; _access = access; - if (isValid()) { + if (!isNull()) { if (_location.check()) { Local::writeFileLocation(mediaKey(), _location); } else { @@ -1439,10 +1485,12 @@ void DocumentData::setWebLocation(const WebFileLocation &location) { _urlLocation = location; } -void DocumentData::collectLocalData(DocumentData *local) { - if (local == this) return; +void DocumentData::collectLocalData(not_null local) { + if (local == this) { + return; + } - _session->data().cache().copyIfEmpty(local->cacheKey(), cacheKey()); + _owner->cache().copyIfEmpty(local->cacheKey(), cacheKey()); if (!local->_data.isEmpty()) { ActiveCache().decrement(_data.size()); _data = local->_data; diff --git a/Telegram/SourceFiles/data/data_document.h b/Telegram/SourceFiles/data/data_document.h index e5186ba49..f17196f74 100644 --- a/Telegram/SourceFiles/data/data_document.h +++ b/Telegram/SourceFiles/data/data_document.h @@ -8,6 +8,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #pragma once #include "data/data_types.h" +#include "ui/image/image.h" namespace Images { class Source; @@ -19,6 +20,10 @@ struct Key; } // namespace Cache } // namespace Storage +namespace Data { +class Session; +} // namespace Data + class AuthSession; class mtpFileLoader; @@ -75,9 +80,10 @@ class Document; class DocumentData { public: - DocumentData(DocumentId id, not_null session); + DocumentData(not_null owner, DocumentId id); - not_null session() const; + [[nodiscard]] Data::Session &owner() const; + [[nodiscard]] AuthSession &session() const; void setattributes( const QVector &attributes); @@ -93,11 +99,11 @@ public: FilePathResolveSaveFromData, FilePathResolveSaveFromDataSilent, }; - bool loaded( + [[nodiscard]] bool loaded( FilePathResolveType type = FilePathResolveCached) const; - bool loading() const; - QString loadingFilePath() const; - bool displayLoading() const; + [[nodiscard]] bool loading() const; + [[nodiscard]] QString loadingFilePath() const; + [[nodiscard]] bool displayLoading() const; void save( Data::FileOrigin origin, const QString &toFile, @@ -106,19 +112,19 @@ public: LoadFromCloudSetting fromCloud = LoadFromCloudOrLocal, bool autoLoading = false); void cancel(); - bool cancelled() const; - float64 progress() const; - int32 loadOffset() const; - bool uploading() const; + [[nodiscard]] bool cancelled() const; + [[nodiscard]] float64 progress() const; + [[nodiscard]] int32 loadOffset() const; + [[nodiscard]] bool uploading() const; void setWaitingForAlbum(); - bool waitingForAlbum() const; + [[nodiscard]] bool waitingForAlbum() const; - QByteArray data() const; - const FileLocation &location(bool check = false) const; + [[nodiscard]] QByteArray data() const; + [[nodiscard]] const FileLocation &location(bool check = false) const; void setLocation(const FileLocation &loc); - QString filepath( + [[nodiscard]] QString filepath( FilePathResolveType type = FilePathResolveCached, bool forceSavingAs = false) const; @@ -127,44 +133,50 @@ public: void performActionOnLoad(); void unload(); - Image *getReplyPreview(Data::FileOrigin origin); + [[nodiscard]] Image *getReplyPreview(Data::FileOrigin origin); - StickerData *sticker() const; - void checkSticker(); - void checkStickerThumb(); - Image *getStickerThumb(); - Image *getStickerImage(); - Data::FileOrigin stickerSetOrigin() const; - Data::FileOrigin stickerOrGifOrigin() const; - bool isStickerSetInstalled() const; - SongData *song(); - const SongData *song() const; - VoiceData *voice(); - const VoiceData *voice() const; + [[nodiscard]] StickerData *sticker() const; + void checkStickerLarge(); + void checkStickerSmall(); + [[nodiscard]] Image *getStickerSmall(); + [[nodiscard]] Image *getStickerLarge(); + [[nodiscard]] Data::FileOrigin stickerSetOrigin() const; + [[nodiscard]] Data::FileOrigin stickerOrGifOrigin() const; + [[nodiscard]] bool isStickerSetInstalled() const; + [[nodiscard]] SongData *song(); + [[nodiscard]] const SongData *song() const; + [[nodiscard]] VoiceData *voice(); + [[nodiscard]] const VoiceData *voice() const; - bool isVoiceMessage() const; - bool isVideoMessage() const; - bool isSong() const; - bool isAudioFile() const; - bool isVideoFile() const; - bool isAnimation() const; - bool isGifv() const; - bool isTheme() const; - bool isSharedMediaMusic() const; - int32 duration() const; - bool isImage() const; + [[nodiscard]] bool isVoiceMessage() const; + [[nodiscard]] bool isVideoMessage() const; + [[nodiscard]] bool isSong() const; + [[nodiscard]] bool isAudioFile() const; + [[nodiscard]] bool isVideoFile() const; + [[nodiscard]] bool isAnimation() const; + [[nodiscard]] bool isGifv() const; + [[nodiscard]] bool isTheme() const; + [[nodiscard]] bool isSharedMediaMusic() const; + [[nodiscard]] int32 duration() const; + [[nodiscard]] bool isImage() const; void recountIsImage(); - bool supportsStreaming() const; + [[nodiscard]] bool supportsStreaming() const; void setData(const QByteArray &data) { _data = data; } bool checkWallPaperProperties(); - bool isWallPaper() const; + [[nodiscard]] bool isWallPaper() const; - bool hasGoodStickerThumb() const; + [[nodiscard]] bool hasThumbnail() const; + void loadThumbnail(Data::FileOrigin origin); + [[nodiscard]] Image *thumbnailInline() const; + [[nodiscard]] Image *thumbnail() const; + void updateThumbnails( + ImagePtr thumbnailInline, + ImagePtr thumbnail); - Image *goodThumbnail() const; - Storage::Cache::Key goodThumbnailCacheKey() const; + [[nodiscard]] Image *goodThumbnail() const; + [[nodiscard]] Storage::Cache::Key goodThumbnailCacheKey() const; void setGoodThumbnail(QImage &&image, QByteArray &&bytes); void refreshGoodThumbnail(); void replaceGoodThumbnail(std::unique_ptr &&source); @@ -175,11 +187,11 @@ public: const QByteArray &fileReference); void setContentUrl(const QString &url); void setWebLocation(const WebFileLocation &location); - bool hasRemoteLocation() const; - bool hasWebLocation() const; - bool isValid() const; - MTPInputDocument mtpInput() const; - QByteArray fileReference() const; + [[nodiscard]] bool hasRemoteLocation() const; + [[nodiscard]] bool hasWebLocation() const; + [[nodiscard]] bool isNull() const; + [[nodiscard]] MTPInputDocument mtpInput() const; + [[nodiscard]] QByteArray fileReference() const; void refreshFileReference(const QByteArray &value); void refreshStickerThumbFileReference(); @@ -187,22 +199,22 @@ public: // (for example for displaying an external inline bot result) // and it has downloaded data, we can collect that data from it // to (this) received from the server "same" document. - void collectLocalData(DocumentData *local); + void collectLocalData(not_null local); - QString filename() const; - QString mimeString() const; - bool hasMimeType(QLatin1String mime) const; + [[nodiscard]] QString filename() const; + [[nodiscard]] QString mimeString() const; + [[nodiscard]] bool hasMimeType(QLatin1String mime) const; void setMimeString(const QString &mime); - MediaKey mediaKey() const; - Storage::Cache::Key cacheKey() const; - uint8 cacheTag() const; + [[nodiscard]] MediaKey mediaKey() const; + [[nodiscard]] Storage::Cache::Key cacheKey() const; + [[nodiscard]] uint8 cacheTag() const; - static QString ComposeNameString( + [[nodiscard]] static QString ComposeNameString( const QString &filename, const QString &songTitle, const QString &songPerformer); - QString composeNameString() const; + [[nodiscard]] QString composeNameString() const; ~DocumentData(); @@ -210,7 +222,6 @@ public: DocumentType type = FileDocument; QSize dimensions; int32 date = 0; - ImagePtr thumb; int32 size = 0; FileStatus status = FileReady; @@ -225,6 +236,8 @@ private: void destroyLoader(mtpFileLoader *newValue = nullptr) const; + [[nodiscard]] bool thumbnailEnoughForSticker() const; + // Two types of location: from MTProto by dc+access or from web by url int32 _dc = 0; uint64 _access = 0; @@ -234,10 +247,12 @@ private: QString _mimeString; WebFileLocation _urlLocation; + ImagePtr _thumbnailInline; + ImagePtr _thumbnail; std::unique_ptr _goodThumbnail; - std::unique_ptr _replyPreview; + Data::ReplyPreview _replyPreview; - not_null _session; + not_null _owner; FileLocation _location; QByteArray _data; diff --git a/Telegram/SourceFiles/data/data_media_types.cpp b/Telegram/SourceFiles/data/data_media_types.cpp index e53c5b304..88028ce79 100644 --- a/Telegram/SourceFiles/data/data_media_types.cpp +++ b/Telegram/SourceFiles/data/data_media_types.cpp @@ -290,7 +290,7 @@ bool MediaPhoto::canBeGrouped() const { } bool MediaPhoto::hasReplyPreview() const { - return !_photo->thumb->isNull(); + return !_photo->isNull(); } Image *MediaPhoto::replyPreview() const { @@ -378,7 +378,7 @@ bool MediaPhoto::updateSentMedia(const MTPMessageMedia &media) { QByteArray bytes; }; const auto saveImageToCache = [&]( - const ImagePtr &image, + not_null image, SizeData size) { Expects(size.location != nullptr); @@ -435,9 +435,9 @@ bool MediaPhoto::updateSentMedia(const MTPMessageMedia &media) { continue; } if (size.letter == 's') { - saveImageToCache(_photo->thumb, size); + saveImageToCache(_photo->thumbnailSmall(), size); } else if (size.letter == 'm') { - saveImageToCache(_photo->medium, size); + saveImageToCache(_photo->thumbnail(), size); } else if (size.letter == 'x' && max < 1) { max = 1; maxSize = size; @@ -450,7 +450,7 @@ bool MediaPhoto::updateSentMedia(const MTPMessageMedia &media) { } } if (maxSize.location) { - saveImageToCache(_photo->full, maxSize); + saveImageToCache(_photo->large(), maxSize); } return true; } @@ -533,7 +533,7 @@ bool MediaFile::canBeGrouped() const { } bool MediaFile::hasReplyPreview() const { - return !_document->thumb->isNull(); + return _document->hasThumbnail(); } Image *MediaFile::replyPreview() const { @@ -1017,9 +1017,9 @@ WebPageData *MediaWebPage::webpage() const { bool MediaWebPage::hasReplyPreview() const { if (const auto document = MediaWebPage::document()) { - return !document->thumb->isNull(); + return document->hasThumbnail(); } else if (const auto photo = MediaWebPage::photo()) { - return !photo->thumb->isNull(); + return !photo->isNull(); } return false; } @@ -1080,9 +1080,9 @@ std::unique_ptr MediaGame::clone(not_null parent) { bool MediaGame::hasReplyPreview() const { if (const auto document = _game->document) { - return !document->thumb->isNull(); + return document->hasThumbnail(); } else if (const auto photo = _game->photo) { - return !photo->thumb->isNull(); + return !photo->isNull(); } return false; } @@ -1182,7 +1182,7 @@ const Invoice *MediaInvoice::invoice() const { bool MediaInvoice::hasReplyPreview() const { if (const auto photo = _invoice.photo) { - return !photo->thumb->isNull(); + return !photo->isNull(); } return false; } diff --git a/Telegram/SourceFiles/data/data_photo.cpp b/Telegram/SourceFiles/data/data_photo.cpp index 749f47cc5..c2aca8c14 100644 --- a/Telegram/SourceFiles/data/data_photo.cpp +++ b/Telegram/SourceFiles/data/data_photo.cpp @@ -12,49 +12,41 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "ui/image/image.h" #include "ui/image/image_source.h" #include "mainwidget.h" -#include "auth_session.h" #include "core/application.h" -PhotoData::PhotoData(const PhotoId &id) -: id(id) { +PhotoData::PhotoData(not_null owner, PhotoId id) +: id(id) +, _owner(owner) { } -PhotoData::PhotoData( - const PhotoId &id, - const uint64 &access, - const QByteArray &fileReference, - TimeId date, - const ImagePtr &thumb, - const ImagePtr &medium, - const ImagePtr &full) -: id(id) -, access(access) -, date(date) -, thumb(thumb) -, medium(medium) -, full(full) { +Data::Session &PhotoData::owner() const { + return *_owner; +} + +AuthSession &PhotoData::session() const { + return _owner->session(); } void PhotoData::automaticLoad( Data::FileOrigin origin, const HistoryItem *item) { - full->automaticLoad(origin, item); + _large->automaticLoad(origin, item); } void PhotoData::automaticLoadSettingsChanged() { - full->automaticLoadSettingsChanged(); + _large->automaticLoadSettingsChanged(); } void PhotoData::download(Data::FileOrigin origin) { - full->loadEvenCancelled(origin); - Auth().data().notifyPhotoLayoutChanged(this); + _large->loadEvenCancelled(origin); + _owner->notifyPhotoLayoutChanged(this); } bool PhotoData::loaded() const { bool wasLoading = loading(); - if (full->loaded()) { + if (_large->loaded()) { if (wasLoading) { - Auth().data().notifyPhotoLayoutChanged(this); + _owner->notifyPhotoLayoutChanged(this); } return true; } @@ -62,18 +54,18 @@ bool PhotoData::loaded() const { } bool PhotoData::loading() const { - return full->loading(); + return _large->loading(); } bool PhotoData::displayLoading() const { - return full->loading() - ? full->displayLoading() + return _large->loading() + ? _large->displayLoading() : (uploading() && !waitingForAlbum()); } void PhotoData::cancel() { - full->cancel(); - Auth().data().notifyPhotoLayoutChanged(this); + _large->cancel(); + _owner->notifyPhotoLayoutChanged(this); } float64 PhotoData::progress() const { @@ -83,7 +75,7 @@ float64 PhotoData::progress() const { } return 0; } - return full->progress(); + return _large->progress(); } void PhotoData::setWaitingForAlbum() { @@ -97,7 +89,7 @@ bool PhotoData::waitingForAlbum() const { } int32 PhotoData::loadOffset() const { - return full->loadOffset(); + return _large->loadOffset(); } bool PhotoData::uploading() const { @@ -105,43 +97,42 @@ bool PhotoData::uploading() const { } void PhotoData::unload() { - // Forget thumb only when image cache limit exceeds. - //thumb->unload(); - medium->unload(); - full->unload(); - _replyPreview = nullptr; + // Forget thumbnail only when image cache limit exceeds. + //_thumbnailInline->unload(); + _thumbnailSmall->unload(); + _thumbnail->unload(); + _large->unload(); + _replyPreview.clear(); } Image *PhotoData::getReplyPreview(Data::FileOrigin origin) { - if (!_replyPreview && !thumb->isNull()) { - const auto previewFromImage = [&](const ImagePtr &image) { - if (!image->loaded()) { - image->load(origin); - return std::unique_ptr(); - } - int w = image->width(), h = image->height(); - if (w <= 0) w = 1; - if (h <= 0) h = 1; - return std::make_unique( - std::make_unique( - (w > h - ? image->pix( - origin, - w * st::msgReplyBarSize.height() / h, - st::msgReplyBarSize.height()) - : image->pix(origin, st::msgReplyBarSize.height()) - ).toImage(), - "PNG")); - }; - if (thumb->isDelayedStorageImage() - && !full->isNull() - && !full->isDelayedStorageImage()) { - _replyPreview = previewFromImage(full); - } else { - _replyPreview = previewFromImage(thumb); + if (_replyPreview + && (_replyPreview.good() || !_thumbnailSmall->loaded())) { + return _replyPreview.image(); + } + if (_thumbnailSmall->isDelayedStorageImage() + && !_large->isNull() + && !_large->isDelayedStorageImage() + && _large->loaded()) { + _replyPreview.prepare( + _large.get(), + origin, + Images::Option(0)); + } else if (_thumbnailSmall->loaded()) { + _replyPreview.prepare( + _thumbnailSmall.get(), + origin, + Images::Option(0)); + } else { + _thumbnailSmall->load(origin); + if (_thumbnailInline) { + _replyPreview.prepare( + _thumbnailInline.get(), + origin, + Images::Option::Blurred); } } - return _replyPreview.get(); + return _replyPreview.image(); } MTPInputPhoto PhotoData::mtpInput() const { @@ -151,19 +142,88 @@ MTPInputPhoto PhotoData::mtpInput() const { MTP_bytes(fileReference)); } -void PhotoData::collectLocalData(PhotoData *local) { - if (local == this) return; +void PhotoData::collectLocalData(not_null local) { + if (local == this) { + return; + } - const auto copyImage = [](const ImagePtr &src, const ImagePtr &dst) { + const auto copyImage = [&](const ImagePtr &src, const ImagePtr &dst) { if (const auto from = src->cacheKey()) { if (const auto to = dst->cacheKey()) { - Auth().data().cache().copyIfEmpty(*from, *to); + _owner->cache().copyIfEmpty(*from, *to); } } }; - copyImage(local->thumb, thumb); - copyImage(local->medium, medium); - copyImage(local->full, full); + copyImage(local->_thumbnailSmall, _thumbnailSmall); + copyImage(local->_thumbnail, _thumbnail); + copyImage(local->_large, _large); +} + +bool PhotoData::isNull() const { + return _large->isNull(); +} + +void PhotoData::loadThumbnail(Data::FileOrigin origin) { + _thumbnail->load(origin); +} + +void PhotoData::loadThumbnailSmall(Data::FileOrigin origin) { + _thumbnailSmall->load(origin); +} + +Image *PhotoData::thumbnailInline() const { + return _thumbnailInline ? _thumbnailInline.get() : nullptr; +} + +not_null PhotoData::thumbnailSmall() const { + return _thumbnailSmall.get(); +} + +not_null PhotoData::thumbnail() const { + return _thumbnail.get(); +} + +void PhotoData::load(Data::FileOrigin origin) { + _large->load(origin); +} + +not_null PhotoData::large() const { + return _large.get(); +} + +void PhotoData::updateImages( + ImagePtr thumbnailInline, + ImagePtr thumbnailSmall, + ImagePtr thumbnail, + ImagePtr large) { + if (!thumbnailSmall || !thumbnail || !large) { + return; + } + 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.isNull()) { + was->setDelayedStorageLocation( + Data::FileOrigin(), + location); + } + } + }; + update(_thumbnailSmall, thumbnailSmall); + update(_thumbnail, thumbnail); + update(_large, large); +} + +int PhotoData::width() const { + return _large->width(); +} + +int PhotoData::height() const { + return _large->height(); } void PhotoOpenClickHandler::onClickImpl() const { diff --git a/Telegram/SourceFiles/data/data_photo.h b/Telegram/SourceFiles/data/data_photo.h index 43be5700f..d668c4d0a 100644 --- a/Telegram/SourceFiles/data/data_photo.h +++ b/Telegram/SourceFiles/data/data_photo.h @@ -9,17 +9,18 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "data/data_types.h" +class AuthSession; + +namespace Data { +class Session; +} // namespace Data + class PhotoData { public: - explicit PhotoData(const PhotoId &id); - PhotoData( - const PhotoId &id, - const uint64 &access, - const QByteArray &fileReference, - TimeId date, - const ImagePtr &thumb, - const ImagePtr &medium, - const ImagePtr &full); + PhotoData(not_null owner, PhotoId id); + + [[nodiscard]] Data::Session &owner() const; + [[nodiscard]] AuthSession &session() const; void automaticLoad( Data::FileOrigin origin, @@ -27,35 +28,53 @@ public: void automaticLoadSettingsChanged(); void download(Data::FileOrigin origin); - bool loaded() const; - bool loading() const; - bool displayLoading() const; + [[nodiscard]] bool loaded() const; + [[nodiscard]] bool loading() const; + [[nodiscard]] bool displayLoading() const; void cancel(); - float64 progress() const; - int32 loadOffset() const; - bool uploading() const; + [[nodiscard]] float64 progress() const; + [[nodiscard]] int32 loadOffset() const; + [[nodiscard]] bool uploading() const; void setWaitingForAlbum(); - bool waitingForAlbum() const; + [[nodiscard]] bool waitingForAlbum() const; void unload(); - Image *getReplyPreview(Data::FileOrigin origin); + [[nodiscard]] Image *getReplyPreview(Data::FileOrigin origin); - MTPInputPhoto mtpInput() const; + [[nodiscard]] MTPInputPhoto mtpInput() const; // When we have some client-side generated photo // (for example for displaying an external inline bot result) // and it has downloaded full image, we can collect image from it // to (this) received from the server "same" photo. - void collectLocalData(PhotoData *local); + void collectLocalData(not_null local); + + bool isNull() const; + + void loadThumbnail(Data::FileOrigin origin); + void loadThumbnailSmall(Data::FileOrigin origin); + Image *thumbnailInline() const; + not_null thumbnailSmall() const; + not_null thumbnail() const; + + void load(Data::FileOrigin origin); + not_null large() 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; uint64 access = 0; QByteArray fileReference; TimeId date = 0; - ImagePtr thumb; - ImagePtr medium; - ImagePtr full; PeerData *peer = nullptr; // for chat and channel photos connection // geo, caption @@ -63,7 +82,14 @@ public: std::unique_ptr uploadingData; private: - std::unique_ptr _replyPreview; + ImagePtr _thumbnailInline; + ImagePtr _thumbnailSmall; + ImagePtr _thumbnail; + ImagePtr _large; + + Data::ReplyPreview _replyPreview; + + not_null _owner; }; diff --git a/Telegram/SourceFiles/data/data_session.cpp b/Telegram/SourceFiles/data/data_session.cpp index 17a5f1cfb..7d01c9c56 100644 --- a/Telegram/SourceFiles/data/data_session.cpp +++ b/Telegram/SourceFiles/data/data_session.cpp @@ -57,23 +57,10 @@ using ViewElement = HistoryView::Element; // b: crop 320x320 // c: crop 640x640 // d: crop 1280x1280 -const auto ThumbLevels = QByteArray::fromRawData("isambcxydw", 10); -const auto MediumLevels = QByteArray::fromRawData("mbcxasydwi", 10); -const auto FullLevels = QByteArray::fromRawData("yxwmsdcbai", 10); - -void UpdateImage(ImagePtr &old, ImagePtr now) { - if (now->isNull()) { - return; - } - if (old->isNull()) { - old = now; - } else if (old->isDelayedStorageImage()) { - const auto location = now->location(); - if (!location.isNull()) { - old->setDelayedStorageLocation(Data::FileOrigin(), location); - } - } -} +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); void CheckForSwitchInlineButton(not_null item) { if (item->out() || !item->hasSwitchInlineButton()) { @@ -121,22 +108,31 @@ QString ExtractUnavailableReason(const QString &restriction) { return QString(); } -MTPPhotoSize FindDocumentThumb(const MTPDdocument &data) { +MTPPhotoSize FindDocumentInlineThumbnail(const MTPDdocument &data) { + const auto &thumbs = data.vthumbs.v; + const auto i = ranges::find( + thumbs, + mtpc_photoStrippedSize, + &MTPPhotoSize::type); + return (i != thumbs.end()) + ? (*i) + : MTPPhotoSize(MTP_photoSizeEmpty(MTP_string(""))); +} + +MTPPhotoSize FindDocumentThumbnail(const MTPDdocument &data) { const auto area = [](const MTPPhotoSize &size) { - static constexpr auto kInvalid = std::numeric_limits::max(); + static constexpr auto kInvalid = 0; return size.match([](const MTPDphotoSizeEmpty &) { return kInvalid; }, [](const MTPDphotoStrippedSize &) { return kInvalid; }, [](const auto &data) { - return (data.vw.v >= 90 || data.vh.v >= 90) - ? (data.vw.v * data.vh.v) - : kInvalid; + return (data.vw.v * data.vh.v); }); }; const auto &thumbs = data.vthumbs.v; - const auto i = ranges::max_element(thumbs, std::greater<>(), area); - return (i != thumbs.end()) + const auto i = ranges::max_element(thumbs, std::less<>(), area); + return (i != thumbs.end() && area(*i) > 0) ? (*i) : MTPPhotoSize(MTP_photoSizeEmpty(MTP_string(""))); } @@ -1609,20 +1605,19 @@ void Session::checkSelfDestructItems() { not_null Session::photo(PhotoId id) { auto i = _photos.find(id); if (i == _photos.end()) { - i = _photos.emplace(id, std::make_unique(id)).first; + i = _photos.emplace( + id, + std::make_unique(this, id)).first; } return i->second.get(); } not_null Session::processPhoto(const MTPPhoto &data) { - switch (data.type()) { - case mtpc_photo: - return processPhoto(data.c_photo()); - - case mtpc_photoEmpty: - return photo(data.c_photoEmpty().vid.v); - } - Unexpected("Type in Session::photo()."); + return data.match([&](const MTPDphoto &data) { + return processPhoto(data); + }, [&](const MTPDphotoEmpty &data) { + return photo(data.vid.v); + }); } not_null Session::processPhoto(const MTPDphoto &data) { @@ -1634,50 +1629,44 @@ not_null Session::processPhoto(const MTPDphoto &data) { not_null Session::processPhoto( const MTPPhoto &data, const PreparedPhotoThumbs &thumbs) { - auto thumb = (const QImage*)nullptr; - auto medium = (const QImage*)nullptr; - auto full = (const QImage*)nullptr; - auto thumbLevel = -1; - auto mediumLevel = -1; - auto fullLevel = -1; - for (auto i = thumbs.cbegin(), e = thumbs.cend(); i != e; ++i) { - const auto newThumbLevel = ThumbLevels.indexOf(i.key()); - const auto newMediumLevel = MediumLevels.indexOf(i.key()); - const auto newFullLevel = FullLevels.indexOf(i.key()); - if (newThumbLevel < 0 || newMediumLevel < 0 || newFullLevel < 0) { - continue; - } - if (thumbLevel < 0 || newThumbLevel < thumbLevel) { - thumbLevel = newThumbLevel; - thumb = &i.value(); - } - if (mediumLevel < 0 || newMediumLevel < mediumLevel) { - mediumLevel = newMediumLevel; - medium = &i.value(); - } - if (fullLevel < 0 || newFullLevel < fullLevel) { - fullLevel = newFullLevel; - full = &i.value(); - } - } - if (!thumb || !medium || !full) { - return photo(0); - } - switch (data.type()) { - case mtpc_photo: - return photo( - data.c_photo().vid.v, - data.c_photo().vaccess_hash.v, - data.c_photo().vfile_reference.v, - data.c_photo().vdate.v, - Images::Create(base::duplicate(*thumb), "JPG"), - Images::Create(base::duplicate(*medium), "JPG"), - Images::Create(base::duplicate(*full), "JPG")); + Expects(!thumbs.empty()); - case mtpc_photoEmpty: - return photo(data.c_photoEmpty().vid.v); - } - Unexpected("Type in Session::photo() with prepared thumbs."); + const auto find = [&](const QByteArray &levels) { + const auto kInvalidIndex = int(levels.size()); + const auto level = [&](const auto &pair) { + const auto letter = pair.first; + const auto index = levels.indexOf(letter); + return (index >= 0) ? index : kInvalidIndex; + }; + const auto result = ranges::max_element( + thumbs, + std::greater<>(), + level); + return (level(*result) == kInvalidIndex) ? thumbs.end() : result; + }; + const auto image = [&](const QByteArray &levels) { + const auto i = find(levels); + return (i == thumbs.end()) + ? ImagePtr() + : Images::Create(base::duplicate(i->second), "JPG"); + }; + const auto thumbnailInline = image(InlineLevels); + const auto thumbnailSmall = image(SmallLevels); + const auto thumbnail = image(ThumbnailLevels); + const auto large = image(LargeLevels); + return data.match([&](const MTPDphoto &data) { + return photo( + data.vid.v, + data.vaccess_hash.v, + data.vfile_reference.v, + data.vdate.v, + thumbnailInline, + thumbnailSmall, + thumbnail, + large); + }, [&](const MTPDphotoEmpty &data) { + return photo(data.vid.v); + }); } not_null Session::photo( @@ -1685,31 +1674,29 @@ not_null Session::photo( const uint64 &access, const QByteArray &fileReference, TimeId date, - const ImagePtr &thumb, - const ImagePtr &medium, - const ImagePtr &full) { + const ImagePtr &thumbnailInline, + const ImagePtr &thumbnailSmall, + const ImagePtr &thumbnail, + const ImagePtr &large) { const auto result = photo(id); photoApplyFields( result, access, fileReference, date, - thumb, - medium, - full); + thumbnailInline, + thumbnailSmall, + thumbnail, + large); return result; } void Session::photoConvert( not_null original, const MTPPhoto &data) { - const auto id = [&] { - switch (data.type()) { - case mtpc_photo: return data.c_photo().vid.v; - case mtpc_photoEmpty: return data.c_photoEmpty().vid.v; - } - Unexpected("Type in Session::photoConvert()."); - }(); + const auto id = data.match([](const auto &data) { + return data.vid.v; + }); if (original->id != id) { auto i = _photos.find(id); if (i == _photos.end()) { @@ -1732,23 +1719,26 @@ void Session::photoConvert( PhotoData *Session::photoFromWeb( const MTPWebDocument &data, - ImagePtr thumb, + ImagePtr thumbnailSmall, bool willBecomeNormal) { - const auto full = Images::Create(data); - if (full->isNull()) { + const auto large = Images::Create(data); + const auto thumbnailInline = ImagePtr(); + if (large->isNull()) { return nullptr; } - auto medium = ImagePtr(); + auto thumbnail = large; if (willBecomeNormal) { - const auto width = full->width(); - const auto height = full->height(); - if (thumb->isNull()) { + const auto width = large->width(); + const auto height = large->height(); + if (thumbnailSmall->isNull()) { auto thumbsize = shrinkToKeepAspect(width, height, 100, 100); - thumb = Images::Create(thumbsize.width(), thumbsize.height()); + thumbnailSmall = Images::Create(thumbsize.width(), thumbsize.height()); } auto mediumsize = shrinkToKeepAspect(width, height, 320, 320); - medium = Images::Create(mediumsize.width(), mediumsize.height()); + thumbnail = Images::Create(mediumsize.width(), mediumsize.height()); + } else if (thumbnailSmall->isNull()) { + thumbnailSmall = large; } return photo( @@ -1756,9 +1746,10 @@ PhotoData *Session::photoFromWeb( uint64(0), QByteArray(), unixtime(), - thumb, - medium, - full); + thumbnailInline, + thumbnailSmall, + thumbnail, + large); } void Session::photoApplyFields( @@ -1772,48 +1763,42 @@ void Session::photoApplyFields( void Session::photoApplyFields( not_null photo, const MTPDphoto &data) { - auto thumb = (const MTPPhotoSize*)nullptr; - auto medium = (const MTPPhotoSize*)nullptr; - auto full = (const MTPPhotoSize*)nullptr; - auto thumbLevel = -1; - auto mediumLevel = -1; - auto fullLevel = -1; - for (const auto &sizeData : data.vsizes.v) { - const auto sizeLetter = sizeData.match([](MTPDphotoSizeEmpty) { - return char(0); - }, [](const auto &size) { - return size.vtype.v.isEmpty() ? char(0) : size.vtype.v[0]; - }); - if (!sizeLetter) continue; - - const auto newThumbLevel = ThumbLevels.indexOf(sizeLetter); - const auto newMediumLevel = MediumLevels.indexOf(sizeLetter); - const auto newFullLevel = FullLevels.indexOf(sizeLetter); - if (newThumbLevel < 0 || newMediumLevel < 0 || newFullLevel < 0) { - continue; - } - if (thumbLevel < 0 || newThumbLevel < thumbLevel) { - thumbLevel = newThumbLevel; - thumb = &sizeData; - } - if (mediumLevel < 0 || newMediumLevel < mediumLevel) { - mediumLevel = newMediumLevel; - medium = &sizeData; - } - if (fullLevel < 0 || newFullLevel < fullLevel) { - fullLevel = newFullLevel; - full = &sizeData; - } - } - if (thumb && medium && full) { + const auto &sizes = data.vsizes.v; + const auto find = [&](const QByteArray &levels) { + const auto kInvalidIndex = int(levels.size()); + const auto level = [&](const MTPPhotoSize &size) { + const auto letter = size.match([](const MTPDphotoSizeEmpty &) { + return char(0); + }, [](const auto &size) { + return size.vtype.v.isEmpty() ? char(0) : size.vtype.v[0]; + }); + const auto index = levels.indexOf(letter); + return (index >= 0) ? index : kInvalidIndex; + }; + const auto result = ranges::max_element( + sizes, + std::greater<>(), + level); + return (level(*result) == kInvalidIndex) ? sizes.end() : result; + }; + const auto image = [&](const QByteArray &levels) { + const auto i = find(levels); + return (i == sizes.end()) ? ImagePtr() : App::image(*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) { photoApplyFields( photo, data.vaccess_hash.v, data.vfile_reference.v, data.vdate.v, - App::image(*thumb), - App::image(*medium), - App::image(*full)); + thumbnailInline, + thumbnailSmall, + thumbnail, + large); } } @@ -1822,18 +1807,21 @@ void Session::photoApplyFields( const uint64 &access, const QByteArray &fileReference, TimeId date, - const ImagePtr &thumb, - const ImagePtr &medium, - const ImagePtr &full) { + const ImagePtr &thumbnailInline, + const ImagePtr &thumbnailSmall, + const ImagePtr &thumbnail, + const ImagePtr &large) { if (!date) { return; } photo->access = access; photo->fileReference = fileReference; photo->date = date; - UpdateImage(photo->thumb, thumb); - UpdateImage(photo->medium, medium); - UpdateImage(photo->full, full); + photo->updateImages( + thumbnailInline, + thumbnailSmall, + thumbnail, + large); } not_null Session::document(DocumentId id) { @@ -1841,7 +1829,7 @@ not_null Session::document(DocumentId id) { if (i == _documents.cend()) { i = _documents.emplace( id, - std::make_unique(id, _session)).first; + std::make_unique(this, id)).first; } return i->second.get(); } @@ -1879,6 +1867,7 @@ not_null Session::processDocument( fields.vdate.v, fields.vattributes.v, qs(fields.vmime_type), + ImagePtr(), Images::Create(std::move(thumb), "JPG"), fields.vdc_id.v, fields.vsize.v, @@ -1895,7 +1884,8 @@ not_null Session::document( TimeId date, const QVector &attributes, const QString &mime, - const ImagePtr &thumb, + const ImagePtr &thumbnailInline, + const ImagePtr &thumbnail, int32 dc, int32 size, const StorageImageLocation &thumbLocation) { @@ -1907,7 +1897,8 @@ not_null Session::document( date, attributes, mime, - thumb, + thumbnailInline, + thumbnail, dc, size, thumbLocation); @@ -1979,6 +1970,7 @@ DocumentData *Session::documentFromWeb( unixtime(), data.vattributes.v, data.vmime_type.v, + ImagePtr(), thumb, MTP::maindc(), int32(0), // data.vsize.v @@ -2000,6 +1992,7 @@ DocumentData *Session::documentFromWeb( unixtime(), data.vattributes.v, data.vmime_type.v, + ImagePtr(), thumb, MTP::maindc(), int32(0), // data.vsize.v @@ -2019,7 +2012,8 @@ void Session::documentApplyFields( void Session::documentApplyFields( not_null document, const MTPDdocument &data) { - const auto thumb = FindDocumentThumb(data); + const auto thumbnailInline = FindDocumentInlineThumbnail(data); + const auto thumbnail = FindDocumentThumbnail(data); documentApplyFields( document, data.vaccess_hash.v, @@ -2027,10 +2021,11 @@ void Session::documentApplyFields( data.vdate.v, data.vattributes.v, qs(data.vmime_type), - App::image(thumb), + App::image(thumbnailInline), + App::image(thumbnail), data.vdc_id.v, data.vsize.v, - StorageImageLocation::FromMTP(thumb)); + StorageImageLocation::FromMTP(thumbnail)); } void Session::documentApplyFields( @@ -2040,7 +2035,8 @@ void Session::documentApplyFields( TimeId date, const QVector &attributes, const QString &mime, - const ImagePtr &thumb, + const ImagePtr &thumbnailInline, + const ImagePtr &thumbnail, int32 dc, int32 size, const StorageImageLocation &thumbLocation) { @@ -2053,13 +2049,7 @@ void Session::documentApplyFields( } document->date = date; document->setMimeString(mime); - if (!thumb->isNull() - && (document->thumb->isNull() - || (document->sticker() - && (document->thumb->width() < thumb->width() - || document->thumb->height() < thumb->height())))) { - document->thumb = thumb; - } + document->updateThumbnails(thumbnailInline, thumbnail); document->size = size; document->recountIsImage(); if (document->sticker() @@ -3085,7 +3075,7 @@ void Session::setWallpapers(const QVector &data, int32 hash) { 0ULL, // access_hash MTPDwallPaper::Flags(0), QString(), // slug - defaultBackground + defaultBackground.get() }); } const auto oldBackground = Images::Create( @@ -3097,7 +3087,7 @@ void Session::setWallpapers(const QVector &data, int32 hash) { 0ULL, // access_hash MTPDwallPaper::Flags(0), QString(), // slug - oldBackground + oldBackground.get() }); } for (const auto &paper : data) { @@ -3109,7 +3099,7 @@ void Session::setWallpapers(const QVector &data, int32 hash) { paper.vaccess_hash.v, paper.vflags.v, qs(paper.vslug), - document->thumb, + document->thumbnail(), document, }); } diff --git a/Telegram/SourceFiles/data/data_session.h b/Telegram/SourceFiles/data/data_session.h index 5063e6ae4..310578ad1 100644 --- a/Telegram/SourceFiles/data/data_session.h +++ b/Telegram/SourceFiles/data/data_session.h @@ -342,15 +342,16 @@ public: const uint64 &access, const QByteArray &fileReference, TimeId date, - const ImagePtr &thumb, - const ImagePtr &medium, - const ImagePtr &full); + const ImagePtr &thumbnailInline, + const ImagePtr &thumbnailSmall, + const ImagePtr &thumbnail, + const ImagePtr &large); void photoConvert( not_null original, const MTPPhoto &data); [[nodiscard]] PhotoData *photoFromWeb( const MTPWebDocument &data, - ImagePtr thumb = ImagePtr(), + ImagePtr thumbnailSmall = ImagePtr(), bool willBecomeNormal = false); [[nodiscard]] not_null document(DocumentId id); @@ -366,7 +367,8 @@ public: TimeId date, const QVector &attributes, const QString &mime, - const ImagePtr &thumb, + const ImagePtr &thumbnailInline, + const ImagePtr &thumbnail, int32 dc, int32 size, const StorageImageLocation &thumbLocation); @@ -571,9 +573,10 @@ private: const uint64 &access, const QByteArray &fileReference, TimeId date, - const ImagePtr &thumb, - const ImagePtr &medium, - const ImagePtr &full); + const ImagePtr &thumbnailInline, + const ImagePtr &thumbnailSmall, + const ImagePtr &thumbnail, + const ImagePtr &large); void documentApplyFields( not_null document, @@ -588,7 +591,8 @@ private: TimeId date, const QVector &attributes, const QString &mime, - const ImagePtr &thumb, + const ImagePtr &thumbnailInline, + const ImagePtr &thumbnail, int32 dc, int32 size, const StorageImageLocation &thumbLocation); diff --git a/Telegram/SourceFiles/data/data_types.cpp b/Telegram/SourceFiles/data/data_types.cpp index f40833f24..735ddd454 100644 --- a/Telegram/SourceFiles/data/data_types.cpp +++ b/Telegram/SourceFiles/data/data_types.cpp @@ -8,6 +8,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "data/data_types.h" #include "data/data_document.h" +#include "data/data_file_origin.h" +#include "ui/image/image_source.h" #include "ui/widgets/input_fields.h" #include "storage/cache/storage_cache_types.h" #include "base/openssl_help.h" @@ -30,6 +32,20 @@ constexpr auto kGeoPointCacheMask = 0x000000FFFFFFFFFFULL; } // namespace +struct ReplyPreview::Data { + Data(std::unique_ptr &&source, bool good); + + Image image; + bool good = false; +}; + +ReplyPreview::Data::Data( + std::unique_ptr &&source, + bool good) +: image(std::move(source)) +, good(good) { +} + Storage::Cache::Key DocumentCacheKey(int32 dcId, uint64 id) { return Storage::Cache::Key{ Data::kDocumentCacheTag | (uint64(dcId) & Data::kDocumentCacheMask), @@ -98,6 +114,63 @@ Storage::Cache::Key GeoPointCacheKey(const GeoPointLocation &location) { }; } +ReplyPreview::ReplyPreview() = default; + +ReplyPreview::ReplyPreview(ReplyPreview &&other) = default; + +ReplyPreview &ReplyPreview::operator=(ReplyPreview &&other) = default; + +ReplyPreview::~ReplyPreview() = default; + +void ReplyPreview::prepare( + not_null image, + FileOrigin origin, + Images::Options options) { + int w = image->width(), h = image->height(); + if (w <= 0) w = 1; + if (h <= 0) h = 1; + auto thumbSize = (w > h) + ? QSize( + w * st::msgReplyBarSize.height() / h, + st::msgReplyBarSize.height()) + : QSize( + st::msgReplyBarSize.height(), + h * st::msgReplyBarSize.height() / w); + thumbSize *= cIntRetinaFactor(); + const auto prepareOptions = Images::Option::Smooth + | Images::Option::TransparentBackground + | options; + auto outerSize = st::msgReplyBarSize.height(); + auto bitmap = image->pixNoCache( + origin, + thumbSize.width(), + thumbSize.height(), + prepareOptions, + outerSize, + outerSize); + _data = std::make_unique( + std::make_unique( + bitmap.toImage(), + "PNG"), + ((options & Images::Option::Blurred) == 0)); +} + +void ReplyPreview::clear() { + _data = nullptr; +} + +Image *ReplyPreview::image() const { + return _data ? &_data->image : nullptr; +} + +bool ReplyPreview::good() const { + return !empty() && _data->good; +} + +bool ReplyPreview::empty() const { + return !_data; +} + } // namespace Data void AudioMsgId::setTypeFromAudio() { diff --git a/Telegram/SourceFiles/data/data_types.h b/Telegram/SourceFiles/data/data_types.h index 826dbc00e..2e78087b5 100644 --- a/Telegram/SourceFiles/data/data_types.h +++ b/Telegram/SourceFiles/data/data_types.h @@ -23,6 +23,11 @@ namespace Ui { class InputField; } // namespace Ui +namespace Images { +enum class Option; +using Options = base::flags