mirror of https://github.com/procxx/kepka.git
				
				
				
			
							parent
							
								
									a70e72f75d
								
							
						
					
					
						commit
						a1baa23a52
					
				|  | @ -894,7 +894,7 @@ void ApiWrap::requestWallPaper( | |||
| 						data.vaccess_hash.v, | ||||
| 						data.vflags.v, | ||||
| 						qs(data.vslug), | ||||
| 						document->thumb, | ||||
| 						document->thumbnail(), | ||||
| 						document | ||||
| 					}); | ||||
| 				} | ||||
|  |  | |||
|  | @ -95,7 +95,7 @@ QImage PrepareScaledFromFull( | |||
| 		size); | ||||
| } | ||||
| 
 | ||||
| QPixmap PrepareScaledFromThumb(ImagePtr thumb) { | ||||
| QPixmap PrepareScaledFromThumb(not_null<Image*> 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)); | ||||
|  |  | |||
|  | @ -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(); | ||||
|  |  | |||
|  | @ -63,7 +63,7 @@ private: | |||
| 
 | ||||
| 	not_null<Window::Controller*> _controller; | ||||
| 	FullMsgId _msgId; | ||||
| 	ImagePtr _thumbnailImage; | ||||
| 	Image *_thumbnailImage = nullptr; | ||||
| 	bool _thumbnailImageLoaded = false; | ||||
| 	Fn<void()> _refreshThumbnail; | ||||
| 	bool _animated = false; | ||||
|  |  | |||
|  | @ -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(), | ||||
|  |  | |||
|  | @ -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); | ||||
| 		} | ||||
| 	} | ||||
|  |  | |||
|  | @ -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<PhotoData*> 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); | ||||
| } | ||||
| 
 | ||||
|  |  | |||
|  | @ -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); | ||||
| 
 | ||||
|  |  | |||
|  | @ -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)); | ||||
| 				} | ||||
| 			} | ||||
|  |  | |||
|  | @ -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()) { | ||||
|  |  | |||
|  | @ -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<StickerIcon> &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; | ||||
|  |  | |||
|  | @ -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<AuthSession*> session) | ||||
| DocumentData::DocumentData(not_null<Data::Session*> owner, DocumentId id) | ||||
| : id(id) | ||||
| , _session(session) { | ||||
| , _owner(owner) { | ||||
| } | ||||
| 
 | ||||
| not_null<AuthSession*> DocumentData::session() const { | ||||
| 	return _session; | ||||
| Data::Session &DocumentData::owner() const { | ||||
| 	return *_owner; | ||||
| } | ||||
| 
 | ||||
| AuthSession &DocumentData::session() const { | ||||
| 	return _owner->session(); | ||||
| } | ||||
| 
 | ||||
| void DocumentData::setattributes(const QVector<MTPDocumentAttribute> &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<Image>( | ||||
| 				std::make_unique<Images::ImageSource>( | ||||
| 					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<DocumentData*> 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; | ||||
|  |  | |||
|  | @ -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<AuthSession*> session); | ||||
| 	DocumentData(not_null<Data::Session*> owner, DocumentId id); | ||||
| 
 | ||||
| 	not_null<AuthSession*> session() const; | ||||
| 	[[nodiscard]] Data::Session &owner() const; | ||||
| 	[[nodiscard]] AuthSession &session() const; | ||||
| 
 | ||||
| 	void setattributes( | ||||
| 		const QVector<MTPDocumentAttribute> &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<Images::Source> &&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<DocumentData*> 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<Image> _goodThumbnail; | ||||
| 	std::unique_ptr<Image> _replyPreview; | ||||
| 	Data::ReplyPreview _replyPreview; | ||||
| 
 | ||||
| 	not_null<AuthSession*> _session; | ||||
| 	not_null<Data::Session*> _owner; | ||||
| 
 | ||||
| 	FileLocation _location; | ||||
| 	QByteArray _data; | ||||
|  |  | |||
|  | @ -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*> 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<Media> MediaGame::clone(not_null<HistoryItem*> 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; | ||||
| } | ||||
|  |  | |||
|  | @ -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<Data::Session*> 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<Image>(); | ||||
| 			} | ||||
| 			int w = image->width(), h = image->height(); | ||||
| 			if (w <= 0) w = 1; | ||||
| 			if (h <= 0) h = 1; | ||||
| 			return std::make_unique<Image>( | ||||
| 				std::make_unique<Images::ImageSource>( | ||||
| 				(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<PhotoData*> 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<Image*> PhotoData::thumbnailSmall() const { | ||||
| 	return _thumbnailSmall.get(); | ||||
| } | ||||
| 
 | ||||
| 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(); | ||||
| } | ||||
| 
 | ||||
| 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 { | ||||
|  |  | |||
|  | @ -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<Data::Session*> 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<PhotoData*> local); | ||||
| 
 | ||||
| 	bool isNull() const; | ||||
| 
 | ||||
| 	void loadThumbnail(Data::FileOrigin origin); | ||||
| 	void loadThumbnailSmall(Data::FileOrigin origin); | ||||
| 	Image *thumbnailInline() const; | ||||
| 	not_null<Image*> thumbnailSmall() const; | ||||
| 	not_null<Image*> thumbnail() const; | ||||
| 
 | ||||
| 	void load(Data::FileOrigin origin); | ||||
| 	not_null<Image*> 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<Data::UploadState> uploadingData; | ||||
| 
 | ||||
| private: | ||||
| 	std::unique_ptr<Image> _replyPreview; | ||||
| 	ImagePtr _thumbnailInline; | ||||
| 	ImagePtr _thumbnailSmall; | ||||
| 	ImagePtr _thumbnail; | ||||
| 	ImagePtr _large; | ||||
| 
 | ||||
| 	Data::ReplyPreview _replyPreview; | ||||
| 
 | ||||
| 	not_null<Data::Session*> _owner; | ||||
| 
 | ||||
| }; | ||||
| 
 | ||||
|  |  | |||
|  | @ -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<HistoryItem*> 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<int>::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<PhotoData*> Session::photo(PhotoId id) { | ||||
| 	auto i = _photos.find(id); | ||||
| 	if (i == _photos.end()) { | ||||
| 		i = _photos.emplace(id, std::make_unique<PhotoData>(id)).first; | ||||
| 		i = _photos.emplace( | ||||
| 			id, | ||||
| 			std::make_unique<PhotoData>(this, id)).first; | ||||
| 	} | ||||
| 	return i->second.get(); | ||||
| } | ||||
| 
 | ||||
| not_null<PhotoData*> 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<PhotoData*> Session::processPhoto(const MTPDphoto &data) { | ||||
|  | @ -1634,50 +1629,44 @@ not_null<PhotoData*> Session::processPhoto(const MTPDphoto &data) { | |||
| not_null<PhotoData*> 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<PhotoData*> Session::photo( | ||||
|  | @ -1685,31 +1674,29 @@ not_null<PhotoData*> 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<PhotoData*> 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<PhotoData*> 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<DocumentData*> Session::document(DocumentId id) { | ||||
|  | @ -1841,7 +1829,7 @@ not_null<DocumentData*> Session::document(DocumentId id) { | |||
| 	if (i == _documents.cend()) { | ||||
| 		i = _documents.emplace( | ||||
| 			id, | ||||
| 			std::make_unique<DocumentData>(id, _session)).first; | ||||
| 			std::make_unique<DocumentData>(this, id)).first; | ||||
| 	} | ||||
| 	return i->second.get(); | ||||
| } | ||||
|  | @ -1879,6 +1867,7 @@ not_null<DocumentData*> 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<DocumentData*> Session::document( | |||
| 		TimeId date, | ||||
| 		const QVector<MTPDocumentAttribute> &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<DocumentData*> 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<DocumentData*> 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<MTPDocumentAttribute> &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<MTPWallPaper> &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<MTPWallPaper> &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<MTPWallPaper> &data, int32 hash) { | |||
| 					paper.vaccess_hash.v, | ||||
| 					paper.vflags.v, | ||||
| 					qs(paper.vslug), | ||||
| 					document->thumb, | ||||
| 					document->thumbnail(), | ||||
| 					document, | ||||
| 				}); | ||||
| 			} | ||||
|  |  | |||
|  | @ -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<PhotoData*> original, | ||||
| 		const MTPPhoto &data); | ||||
| 	[[nodiscard]] PhotoData *photoFromWeb( | ||||
| 		const MTPWebDocument &data, | ||||
| 		ImagePtr thumb = ImagePtr(), | ||||
| 		ImagePtr thumbnailSmall = ImagePtr(), | ||||
| 		bool willBecomeNormal = false); | ||||
| 
 | ||||
| 	[[nodiscard]] not_null<DocumentData*> document(DocumentId id); | ||||
|  | @ -366,7 +367,8 @@ public: | |||
| 		TimeId date, | ||||
| 		const QVector<MTPDocumentAttribute> &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<DocumentData*> document, | ||||
|  | @ -588,7 +591,8 @@ private: | |||
| 		TimeId date, | ||||
| 		const QVector<MTPDocumentAttribute> &attributes, | ||||
| 		const QString &mime, | ||||
| 		const ImagePtr &thumb, | ||||
| 		const ImagePtr &thumbnailInline, | ||||
| 		const ImagePtr &thumbnail, | ||||
| 		int32 dc, | ||||
| 		int32 size, | ||||
| 		const StorageImageLocation &thumbLocation); | ||||
|  |  | |||
|  | @ -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<Images::Source> &&source, bool good); | ||||
| 
 | ||||
| 	Image image; | ||||
| 	bool good = false; | ||||
| }; | ||||
| 
 | ||||
| ReplyPreview::Data::Data( | ||||
| 	std::unique_ptr<Images::Source> &&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*> 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<ReplyPreview::Data>( | ||||
| 		std::make_unique<Images::ImageSource>( | ||||
| 			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() { | ||||
|  |  | |||
|  | @ -23,6 +23,11 @@ namespace Ui { | |||
| class InputField; | ||||
| } // namespace Ui
 | ||||
| 
 | ||||
| namespace Images { | ||||
| enum class Option; | ||||
| using Options = base::flags<Option>; | ||||
| } // namespace Images
 | ||||
| 
 | ||||
| class StorageImageLocation; | ||||
| class WebFileLocation; | ||||
| struct GeoPointLocation; | ||||
|  | @ -50,6 +55,35 @@ constexpr auto kVoiceMessageCacheTag = uint8(0x03); | |||
| constexpr auto kVideoMessageCacheTag = uint8(0x04); | ||||
| constexpr auto kAnimationCacheTag = uint8(0x05); | ||||
| 
 | ||||
| struct FileOrigin; | ||||
| 
 | ||||
| class ReplyPreview { | ||||
| public: | ||||
| 	ReplyPreview(); | ||||
| 	ReplyPreview(ReplyPreview &&other); | ||||
| 	ReplyPreview &operator=(ReplyPreview &&other); | ||||
| 	~ReplyPreview(); | ||||
| 
 | ||||
| 	void prepare( | ||||
| 		not_null<Image*> image, | ||||
| 		FileOrigin origin, | ||||
| 		Images::Options options); | ||||
| 	void clear(); | ||||
| 
 | ||||
| 	[[nodiscard]] Image *image() const; | ||||
| 	[[nodiscard]] bool good() const; | ||||
| 	[[nodiscard]] bool empty() const; | ||||
| 
 | ||||
| 	[[nodiscard]] explicit operator bool() const { | ||||
| 		return !empty(); | ||||
| 	} | ||||
| 
 | ||||
| private: | ||||
| 	struct Data; | ||||
| 	std::unique_ptr<Data> _data; | ||||
| 
 | ||||
| }; | ||||
| 
 | ||||
| } // namespace Data
 | ||||
| 
 | ||||
| struct MessageGroupId { | ||||
|  | @ -273,7 +307,7 @@ using PollId = uint64; | |||
| using WallPaperId = uint64; | ||||
| constexpr auto CancelledWebPageId = WebPageId(0xFFFFFFFFFFFFFFFFULL); | ||||
| 
 | ||||
| using PreparedPhotoThumbs = QMap<char, QImage>; | ||||
| using PreparedPhotoThumbs = base::flat_map<char, QImage>; | ||||
| 
 | ||||
| // [0] == -1 -- counting, [0] == -2 -- could not count
 | ||||
| using VoiceWaveform = QVector<signed char>; | ||||
|  |  | |||
|  | @ -63,7 +63,7 @@ WebPageCollage ExtractCollage( | |||
| 	for (const auto &item : items) { | ||||
| 		const auto good = item.match([&](const MTPDpageBlockPhoto &data) { | ||||
| 			const auto photo = storage.photo(data.vphoto_id.v); | ||||
| 			if (photo->full->isNull()) { | ||||
| 			if (photo->isNull()) { | ||||
| 				return false; | ||||
| 			} | ||||
| 			result.items.push_back(photo); | ||||
|  | @ -231,12 +231,12 @@ void WebPageData::replaceDocumentGoodThumbnail() { | |||
| 	if (!document || !photo || !document->goodThumbnail()) { | ||||
| 		return; | ||||
| 	} | ||||
| 	const auto &location = photo->full->location(); | ||||
| 	const auto &location = photo->large()->location(); | ||||
| 	if (!location.isNull()) { | ||||
| 		document->replaceGoodThumbnail( | ||||
| 			std::make_unique<Images::StorageSource>( | ||||
| 				location, | ||||
| 				photo->full->bytesSize())); | ||||
| 				photo->large()->bytesSize())); | ||||
| 	} | ||||
| 
 | ||||
| } | ||||
|  |  | |||
|  | @ -1074,7 +1074,7 @@ void InnerWidget::showContextMenu(QContextMenuEvent *e, bool showFromTouch) { | |||
| } | ||||
| 
 | ||||
| void InnerWidget::savePhotoToFile(PhotoData *photo) { | ||||
| 	if (!photo || !photo->date || !photo->loaded()) { | ||||
| 	if (!photo || photo->isNull() || !photo->loaded()) { | ||||
| 		return; | ||||
| 	} | ||||
| 
 | ||||
|  | @ -1086,7 +1086,7 @@ void InnerWidget::savePhotoToFile(PhotoData *photo) { | |||
| 		filedialogDefaultName(qsl("photo"), qsl(".jpg")), | ||||
| 		crl::guard(this, [=](const QString &result) { | ||||
| 			if (!result.isEmpty()) { | ||||
| 				photo->full->pix(Data::FileOrigin()).toImage().save(result, "JPG"); | ||||
| 				photo->large()->original().save(result, "JPG"); | ||||
| 			} | ||||
| 		})); | ||||
| } | ||||
|  | @ -1096,9 +1096,9 @@ void InnerWidget::saveDocumentToFile(DocumentData *document) { | |||
| } | ||||
| 
 | ||||
| void InnerWidget::copyContextImage(PhotoData *photo) { | ||||
| 	if (!photo || !photo->date || !photo->loaded()) return; | ||||
| 	if (!photo || photo->isNull() || !photo->loaded()) return; | ||||
| 
 | ||||
| 	QApplication::clipboard()->setPixmap(photo->full->pix(Data::FileOrigin())); | ||||
| 	QApplication::clipboard()->setImage(photo->large()->original()); | ||||
| } | ||||
| 
 | ||||
| void InnerWidget::copySelectedText() { | ||||
|  |  | |||
|  | @ -1771,7 +1771,7 @@ void HistoryInner::copySelectedText() { | |||
| } | ||||
| 
 | ||||
| void HistoryInner::savePhotoToFile(not_null<PhotoData*> photo) { | ||||
| 	if (!photo->date || !photo->loaded()) return; | ||||
| 	if (photo->isNull() || !photo->loaded()) return; | ||||
| 
 | ||||
| 	auto filter = qsl("JPEG Image (*.jpg);;") + FileDialog::AllFilesFilter(); | ||||
| 	FileDialog::GetWritePath( | ||||
|  | @ -1783,15 +1783,15 @@ void HistoryInner::savePhotoToFile(not_null<PhotoData*> photo) { | |||
| 			qsl(".jpg")), | ||||
| 		crl::guard(this, [=](const QString &result) { | ||||
| 			if (!result.isEmpty()) { | ||||
| 				photo->full->pix(Data::FileOrigin()).toImage().save(result, "JPG"); | ||||
| 				photo->large()->original().save(result, "JPG"); | ||||
| 			} | ||||
| 		})); | ||||
| } | ||||
| 
 | ||||
| void HistoryInner::copyContextImage(not_null<PhotoData*> photo) { | ||||
| 	if (!photo->date || !photo->loaded()) return; | ||||
| 	if (photo->isNull() || !photo->loaded()) return; | ||||
| 
 | ||||
| 	QApplication::clipboard()->setPixmap(photo->full->pix(Data::FileOrigin())); | ||||
| 	QApplication::clipboard()->setImage(photo->large()->original()); | ||||
| } | ||||
| 
 | ||||
| void HistoryInner::showStickerPackInfo(not_null<DocumentData*> document) { | ||||
|  |  | |||
|  | @ -6361,7 +6361,7 @@ void HistoryWidget::drawField(Painter &p, const QRect &rect) { | |||
| 	if (drawWebPagePreview) { | ||||
| 		auto previewLeft = st::historyReplySkip + st::webPageLeft; | ||||
| 		p.fillRect(st::historyReplySkip, backy + st::msgReplyPadding.top(), st::webPageBar, st::msgReplyBarSize.height(), st::msgInReplyBarColor); | ||||
| 		if ((_previewData->photo && !_previewData->photo->thumb->isNull()) || (_previewData->document && !_previewData->document->thumb->isNull())) { | ||||
| 		if ((_previewData->photo && !_previewData->photo->isNull()) || (_previewData->document && _previewData->document->hasThumbnail())) { | ||||
| 			const auto preview = _previewData->photo | ||||
| 				? _previewData->photo->getReplyPreview(Data::FileOrigin()) | ||||
| 				: _previewData->document->getReplyPreview(Data::FileOrigin()); | ||||
|  |  | |||
|  | @ -69,12 +69,13 @@ void HistoryDocument::createComponents(bool caption) { | |||
| 		mask |= HistoryDocumentVoice::Bit(); | ||||
| 	} else { | ||||
| 		mask |= HistoryDocumentNamed::Bit(); | ||||
| 		if (!_data->isSong() | ||||
| 			&& !_data->thumb->isNull() | ||||
| 			&& _data->thumb->width() | ||||
| 			&& _data->thumb->height() | ||||
| 			&& !Data::IsExecutableName(_data->filename())) { | ||||
| 			mask |= HistoryDocumentThumbed::Bit(); | ||||
| 		if (const auto thumb = _data->thumbnail()) { | ||||
| 			if (!_data->isSong() | ||||
| 				&& thumb->width() | ||||
| 				&& thumb->height() | ||||
| 				&& !Data::IsExecutableName(_data->filename())) { | ||||
| 				mask |= HistoryDocumentThumbed::Bit(); | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| 	if (caption) { | ||||
|  | @ -117,9 +118,9 @@ QSize HistoryDocument::countOptimalSize() { | |||
| 	} | ||||
| 	auto thumbed = Get<HistoryDocumentThumbed>(); | ||||
| 	if (thumbed) { | ||||
| 		_data->thumb->load(_realParent->fullId()); | ||||
| 		auto tw = ConvertScale(_data->thumb->width()); | ||||
| 		auto th = ConvertScale(_data->thumb->height()); | ||||
| 		_data->loadThumbnail(_realParent->fullId()); | ||||
| 		auto tw = ConvertScale(_data->thumbnail()->width()); | ||||
| 		auto th = ConvertScale(_data->thumbnail()->height()); | ||||
| 		if (tw > th) { | ||||
| 			thumbed->_thumbw = (tw * st::msgFileThumbSize) / th; | ||||
| 		} else { | ||||
|  | @ -232,10 +233,12 @@ void HistoryDocument::draw(Painter &p, const QRect &r, TextSelection selection, | |||
| 		auto roundRadius = inWebPage ? ImageRoundRadius::Small : ImageRoundRadius::Large; | ||||
| 		QRect rthumb(rtlrect(st::msgFileThumbPadding.left(), st::msgFileThumbPadding.top() - topMinus, st::msgFileThumbSize, st::msgFileThumbSize, width())); | ||||
| 		QPixmap thumb; | ||||
| 		if (loaded) { | ||||
| 			thumb = _data->thumb->pixSingle(_realParent->fullId(), thumbed->_thumbw, 0, st::msgFileThumbSize, st::msgFileThumbSize, roundRadius); | ||||
| 		} else { | ||||
| 			thumb = _data->thumb->pixBlurredSingle(_realParent->fullId(), thumbed->_thumbw, 0, st::msgFileThumbSize, st::msgFileThumbSize, roundRadius); | ||||
| 		if (const auto normal = _data->thumbnail()) { | ||||
| 			if (normal->loaded()) { | ||||
| 				thumb = normal->pixSingle(_realParent->fullId(), thumbed->_thumbw, 0, st::msgFileThumbSize, st::msgFileThumbSize, roundRadius); | ||||
| 			} else if (const auto blurred = _data->thumbnailInline()) { | ||||
| 				thumb = blurred->pixBlurredSingle(_realParent->fullId(), thumbed->_thumbw, 0, st::msgFileThumbSize, st::msgFileThumbSize, roundRadius); | ||||
| 			} | ||||
| 		} | ||||
| 		p.drawPixmap(rthumb.topLeft(), thumb); | ||||
| 		if (selected) { | ||||
|  | @ -528,7 +531,7 @@ TextState HistoryDocument::textState(QPoint point, StateRequest request) const { | |||
| 			painth -= st::msgPadding.bottom(); | ||||
| 		} | ||||
| 	} | ||||
| 	if (QRect(0, 0, width(), painth).contains(point) && !_data->loading() && !_data->uploading() && _data->isValid()) { | ||||
| 	if (QRect(0, 0, width(), painth).contains(point) && !_data->loading() && !_data->uploading() && !_data->isNull()) { | ||||
| 		result.link = _openl; | ||||
| 		return result; | ||||
| 	} | ||||
|  |  | |||
|  | @ -56,7 +56,7 @@ HistoryGif::HistoryGif( | |||
| 	setStatusSize(FileStatusSizeReady); | ||||
| 
 | ||||
| 	_caption = createCaption(item); | ||||
| 	_data->thumb->load(item->fullId()); | ||||
| 	_data->loadThumbnail(item->fullId()); | ||||
| } | ||||
| 
 | ||||
| QSize HistoryGif::countOptimalSize() { | ||||
|  | @ -89,8 +89,8 @@ QSize HistoryGif::countOptimalSize() { | |||
| 	} else { | ||||
| 		tw = ConvertScale(_data->dimensions.width()), th = ConvertScale(_data->dimensions.height()); | ||||
| 		if (!tw || !th) { | ||||
| 			tw = ConvertScale(_data->thumb->width()); | ||||
| 			th = ConvertScale(_data->thumb->height()); | ||||
| 			tw = ConvertScale(_data->thumbnail()->width()); | ||||
| 			th = ConvertScale(_data->thumbnail()->height()); | ||||
| 		} | ||||
| 	} | ||||
| 	const auto maxSize = _data->isVideoMessage() | ||||
|  | @ -147,8 +147,8 @@ QSize HistoryGif::countCurrentSize(int newWidth) { | |||
| 	} else { | ||||
| 		tw = ConvertScale(_data->dimensions.width()), th = ConvertScale(_data->dimensions.height()); | ||||
| 		if (!tw || !th) { | ||||
| 			tw = ConvertScale(_data->thumb->width()); | ||||
| 			th = ConvertScale(_data->thumb->height()); | ||||
| 			tw = ConvertScale(_data->thumbnail()->width()); | ||||
| 			th = ConvertScale(_data->thumbnail()->height()); | ||||
| 		} | ||||
| 	} | ||||
| 	const auto maxSize = _data->isVideoMessage() | ||||
|  | @ -347,7 +347,13 @@ void HistoryGif::draw(Painter &p, const QRect &r, TextSelection selection, TimeM | |||
| 			if (good) { | ||||
| 				good->load({}); | ||||
| 			} | ||||
| 			p.drawPixmap(rthumb.topLeft(), _data->thumb->pixBlurredSingle(_realParent->fullId(), _thumbw, _thumbh, usew, painth, roundRadius, roundCorners)); | ||||
| 			if (const auto normal = _data->thumbnail()) { | ||||
| 				if (normal->loaded()) { | ||||
| 					p.drawPixmap(rthumb.topLeft(), _data->thumbnail()->pixSingle(_realParent->fullId(), _thumbw, _thumbh, usew, painth, roundRadius, roundCorners)); | ||||
| 				} else if (const auto blurred = _data->thumbnailInline()) { | ||||
| 					p.drawPixmap(rthumb.topLeft(), blurred->pixBlurredSingle(_realParent->fullId(), _thumbw, _thumbh, usew, painth, roundRadius, roundCorners)); | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
|  |  | |||
|  | @ -59,7 +59,11 @@ void HistoryPhoto::create(FullMsgId contextId, PeerData *chat) { | |||
| 		std::make_shared<PhotoOpenClickHandler>(_data, contextId, chat), | ||||
| 		std::make_shared<PhotoSaveClickHandler>(_data, contextId, chat), | ||||
| 		std::make_shared<PhotoCancelClickHandler>(_data, contextId, chat)); | ||||
| 	_data->thumb->load(contextId); | ||||
| 	if (!_data->thumbnailInline() | ||||
| 		&& !_data->loaded() | ||||
| 		&& !_data->thumbnail()->loaded()) { | ||||
| 		_data->thumbnailSmall()->load(contextId); | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| QSize HistoryPhoto::countOptimalSize() { | ||||
|  | @ -74,8 +78,8 @@ QSize HistoryPhoto::countOptimalSize() { | |||
| 	auto maxWidth = 0; | ||||
| 	auto minHeight = 0; | ||||
| 
 | ||||
| 	auto tw = ConvertScale(_data->full->width()); | ||||
| 	auto th = ConvertScale(_data->full->height()); | ||||
| 	auto tw = ConvertScale(_data->width()); | ||||
| 	auto th = ConvertScale(_data->height()); | ||||
| 	if (!tw || !th) { | ||||
| 		tw = th = 1; | ||||
| 	} | ||||
|  | @ -106,7 +110,7 @@ QSize HistoryPhoto::countOptimalSize() { | |||
| } | ||||
| 
 | ||||
| QSize HistoryPhoto::countCurrentSize(int newWidth) { | ||||
| 	int tw = ConvertScale(_data->full->width()), th = ConvertScale(_data->full->height()); | ||||
| 	int tw = ConvertScale(_data->width()), th = ConvertScale(_data->height()); | ||||
| 	if (tw > st::maxMediaSize) { | ||||
| 		th = (st::maxMediaSize * th) / tw; | ||||
| 		tw = st::maxMediaSize; | ||||
|  | @ -169,9 +173,19 @@ void HistoryPhoto::draw(Painter &p, const QRect &r, TextSelection selection, Tim | |||
| 
 | ||||
| 	auto rthumb = rtlrect(paintx, painty, paintw, painth, width()); | ||||
| 	if (_serviceWidth > 0) { | ||||
| 		const auto pix = loaded | ||||
| 			? _data->full->pixCircled(_realParent->fullId(), _pixw, _pixh) | ||||
| 			: _data->thumb->pixBlurredCircled(_realParent->fullId(), _pixw, _pixh); | ||||
| 		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()) { | ||||
| 				return blurred->pixBlurredCircled(_realParent->fullId(), _pixw, _pixh); | ||||
| 			} else { | ||||
| 				return QPixmap(); | ||||
| 			} | ||||
| 		}(); | ||||
| 		p.drawPixmap(rthumb.topLeft(), pix); | ||||
| 	} else { | ||||
| 		if (bubble) { | ||||
|  | @ -189,9 +203,19 @@ void HistoryPhoto::draw(Painter &p, const QRect &r, TextSelection selection, Tim | |||
| 		auto roundRadius = inWebPage ? ImageRoundRadius::Small : ImageRoundRadius::Large; | ||||
| 		auto roundCorners = inWebPage ? RectPart::AllCorners : ((isBubbleTop() ? (RectPart::TopLeft | RectPart::TopRight) : RectPart::None) | ||||
| 			| ((isBubbleBottom() && _caption.isEmpty()) ? (RectPart::BottomLeft | RectPart::BottomRight) : RectPart::None)); | ||||
| 		const auto pix = loaded | ||||
| 			? _data->full->pixSingle(_realParent->fullId(), _pixw, _pixh, paintw, painth, roundRadius, roundCorners) | ||||
| 			: _data->thumb->pixBlurredSingle(_realParent->fullId(), _pixw, _pixh, paintw, painth, roundRadius, roundCorners); | ||||
| 		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()) { | ||||
| 				return blurred->pixBlurredSingle(_realParent->fullId(), _pixw, _pixh, paintw, painth, roundRadius, roundCorners); | ||||
| 			} else { | ||||
| 				return QPixmap(); | ||||
| 			} | ||||
| 		}(); | ||||
| 		p.drawPixmap(rthumb.topLeft(), pix); | ||||
| 		if (selected) { | ||||
| 			App::complexOverlayRect(p, rthumb, roundRadius, roundCorners); | ||||
|  | @ -224,7 +248,7 @@ void HistoryPhoto::draw(Painter &p, const QRect &r, TextSelection selection, Tim | |||
| 		auto icon = ([radial, this, selected]() -> const style::icon* { | ||||
| 			if (radial || _data->loading()) { | ||||
| 				if (_data->uploading() | ||||
| 					|| !_data->full->location().isNull()) { | ||||
| 					|| !_data->large()->location().isNull()) { | ||||
| 					return &(selected ? st::historyFileThumbCancelSelected : st::historyFileThumbCancel); | ||||
| 				} | ||||
| 				return nullptr; | ||||
|  | @ -292,7 +316,7 @@ TextState HistoryPhoto::textState(QPoint point, StateRequest request) const { | |||
| 		} else if (_data->loaded()) { | ||||
| 			result.link = _openl; | ||||
| 		} else if (_data->loading()) { | ||||
| 			if (!_data->full->location().isNull()) { | ||||
| 			if (!_data->large()->location().isNull()) { | ||||
| 				result.link = _cancell; | ||||
| 			} | ||||
| 		} else { | ||||
|  | @ -317,8 +341,8 @@ TextState HistoryPhoto::textState(QPoint point, StateRequest request) const { | |||
| } | ||||
| 
 | ||||
| QSize HistoryPhoto::sizeForGrouping() const { | ||||
| 	const auto width = _data->full->width(); | ||||
| 	const auto height = _data->full->height(); | ||||
| 	const auto width = _data->width(); | ||||
| 	const auto height = _data->height(); | ||||
| 	return { std::max(width, 1), std::max(height, 1) }; | ||||
| } | ||||
| 
 | ||||
|  | @ -395,7 +419,7 @@ void HistoryPhoto::drawGrouped( | |||
| 			if (_data->waitingForAlbum()) { | ||||
| 				return &(selected ? st::historyFileThumbWaitingSelected : st::historyFileThumbWaiting); | ||||
| 			} else if (radial || _data->loading()) { | ||||
| 				if (_data->uploading() || !_data->full->location().isNull()) { | ||||
| 				if (_data->uploading() || !_data->large()->location().isNull()) { | ||||
| 					return &(selected ? st::historyFileThumbCancelSelected : st::historyFileThumbCancel); | ||||
| 				} | ||||
| 				return nullptr; | ||||
|  | @ -440,7 +464,7 @@ TextState HistoryPhoto::getStateGrouped( | |||
| 		: _data->loaded() | ||||
| 		? _openl | ||||
| 		: _data->loading() | ||||
| 		? (_data->full->location().isNull() | ||||
| 		? (_data->large()->location().isNull() | ||||
| 			? ClickHandlerPtr() | ||||
| 			: _cancell) | ||||
| 		: _savel); | ||||
|  | @ -470,7 +494,13 @@ void HistoryPhoto::validateGroupedCache( | |||
| 		not_null<QPixmap*> cache) const { | ||||
| 	using Option = Images::Option; | ||||
| 	const auto loaded = _data->loaded(); | ||||
| 	const auto loadLevel = loaded ? 2 : _data->thumb->loaded() ? 1 : 0; | ||||
| 	const auto loadLevel = loaded | ||||
| 		? 2 | ||||
| 		: (_data->thumbnailInline() | ||||
| 			|| _data->thumbnail()->loaded() | ||||
| 			|| _data->thumbnailSmall()->loaded()) | ||||
| 		? 1 | ||||
| 		: 0; | ||||
| 	const auto width = geometry.width(); | ||||
| 	const auto height = geometry.height(); | ||||
| 	const auto options = Option::Smooth | ||||
|  | @ -488,14 +518,22 @@ void HistoryPhoto::validateGroupedCache( | |||
| 		return; | ||||
| 	} | ||||
| 
 | ||||
| 	const auto originalWidth = ConvertScale(_data->full->width()); | ||||
| 	const auto originalHeight = ConvertScale(_data->full->height()); | ||||
| 	const auto originalWidth = ConvertScale(_data->width()); | ||||
| 	const auto originalHeight = ConvertScale(_data->height()); | ||||
| 	const auto pixSize = Ui::GetImageScaleSizeForGeometry( | ||||
| 		{ originalWidth, originalHeight }, | ||||
| 		{ width, height }); | ||||
| 	const auto pixWidth = pixSize.width() * cIntRetinaFactor(); | ||||
| 	const auto pixHeight = pixSize.height() * cIntRetinaFactor(); | ||||
| 	const auto &image = loaded ? _data->full : _data->thumb; | ||||
| 	const auto image = loaded | ||||
| 		? _data->large().get() | ||||
| 		: _data->thumbnail()->loaded() | ||||
| 		? _data->thumbnail().get() | ||||
| 		: _data->thumbnailSmall()->loaded() | ||||
| 		? _data->thumbnailSmall().get() | ||||
| 		: _data->thumbnailInline() | ||||
| 		? _data->thumbnailInline() | ||||
| 		: Image::Blank().get(); | ||||
| 
 | ||||
| 	*cacheKey = key; | ||||
| 	*cache = image->pixNoCache(_realParent->fullId(), pixWidth, pixHeight, options, width, height); | ||||
|  |  | |||
|  | @ -30,8 +30,8 @@ HistorySticker::HistorySticker( | |||
| : HistoryMedia(parent) | ||||
| , _data(document) | ||||
| , _emoji(_data->sticker()->alt) { | ||||
| 	_data->thumb->load(parent->data()->fullId()); | ||||
| 	if (auto emoji = Ui::Emoji::Find(_emoji)) { | ||||
| 	_data->loadThumbnail(parent->data()->fullId()); | ||||
| 	if (const auto emoji = Ui::Emoji::Find(_emoji)) { | ||||
| 		_emoji = emoji->text(); | ||||
| 	} | ||||
| } | ||||
|  | @ -93,7 +93,7 @@ void HistorySticker::draw(Painter &p, const QRect &r, TextSelection selection, T | |||
| 
 | ||||
| 	if (width() < st::msgPadding.left() + st::msgPadding.right() + 1) return; | ||||
| 
 | ||||
| 	_data->checkSticker(); | ||||
| 	_data->checkStickerLarge(); | ||||
| 	bool loaded = _data->loaded(); | ||||
| 	bool selected = (selection == FullSelection); | ||||
| 
 | ||||
|  | @ -117,14 +117,25 @@ void HistorySticker::draw(Painter &p, const QRect &r, TextSelection selection, T | |||
| 		const auto w = _pixw; | ||||
| 		const auto h = _pixh; | ||||
| 		const auto &c = st::msgStickerOverlay; | ||||
| 		if (const auto image = _data->getStickerImage()) { | ||||
| 		if (const auto image = _data->getStickerLarge()) { | ||||
| 			return selected | ||||
| 				? image->pixColored(o, c, w, h) | ||||
| 				: image->pix(o, w, h); | ||||
| 		//
 | ||||
| 		// Inline thumbnails can't have alpha channel.
 | ||||
| 		//
 | ||||
| 		//} else if (const auto blurred = _data->thumbnailInline()) {
 | ||||
| 		//	return selected
 | ||||
| 		//		? blurred->pixBlurredColored(o, c, w, h)
 | ||||
| 		//		: blurred->pixBlurred(o, w, h);
 | ||||
| 		} else if (const auto thumbnail = _data->thumbnail()) { | ||||
| 			return selected | ||||
| 				? thumbnail->pixBlurredColored(o, c, w, h) | ||||
| 				: thumbnail->pixBlurred(o, w, h); | ||||
| 		} else { | ||||
| 			static QPixmap empty; | ||||
| 			return empty; | ||||
| 		} | ||||
| 		return selected | ||||
| 			? _data->thumb->pixBlurredColored(o, c, w, h) | ||||
| 			: _data->thumb->pixBlurred(o, w, h); | ||||
| 	}(); | ||||
| 	p.drawPixmap( | ||||
| 		QPoint{ usex + (usew - _pixw) / 2, (minHeight() - _pixh) / 2 }, | ||||
|  |  | |||
|  | @ -41,7 +41,21 @@ HistoryVideo::HistoryVideo( | |||
| 
 | ||||
| 	setStatusSize(FileStatusSizeReady); | ||||
| 
 | ||||
| 	_data->thumb->load(realParent->fullId()); | ||||
| 	_data->loadThumbnail(realParent->fullId()); | ||||
| } | ||||
| 
 | ||||
| QSize HistoryVideo::sizeForAspectRatio() const { | ||||
| 	// We use size only for aspect ratio and we want to have it
 | ||||
| 	// as close to the thumbnail as possible.
 | ||||
| 	//if (!_data->dimensions.isEmpty()) {
 | ||||
| 	//	return _data->dimensions;
 | ||||
| 	//}
 | ||||
| 	if (const auto thumb = _data->thumbnail()) { | ||||
| 		if (!thumb->size().isEmpty()) { | ||||
| 			return thumb->size(); | ||||
| 		} | ||||
| 	} | ||||
| 	return { 1, 1 }; | ||||
| } | ||||
| 
 | ||||
| QSize HistoryVideo::countOptimalSize() { | ||||
|  | @ -53,8 +67,9 @@ QSize HistoryVideo::countOptimalSize() { | |||
| 			_parent->skipBlockHeight()); | ||||
| 	} | ||||
| 
 | ||||
| 	auto tw = ConvertScale(_data->thumb->width()); | ||||
| 	auto th = ConvertScale(_data->thumb->height()); | ||||
| 	const auto size = sizeForAspectRatio(); | ||||
| 	auto tw = ConvertScale(size.width()); | ||||
| 	auto th = ConvertScale(size.height()); | ||||
| 	if (!tw || !th) { | ||||
| 		tw = th = 1; | ||||
| 	} | ||||
|  | @ -86,7 +101,9 @@ QSize HistoryVideo::countOptimalSize() { | |||
| } | ||||
| 
 | ||||
| QSize HistoryVideo::countCurrentSize(int newWidth) { | ||||
| 	int tw = ConvertScale(_data->thumb->width()), th = ConvertScale(_data->thumb->height()); | ||||
| 	const auto size = sizeForAspectRatio(); | ||||
| 	auto tw = ConvertScale(size.width()); | ||||
| 	auto th = ConvertScale(size.height()); | ||||
| 	if (!tw || !th) { | ||||
| 		tw = th = 1; | ||||
| 	} | ||||
|  | @ -166,7 +183,12 @@ void HistoryVideo::draw(Painter &p, const QRect &r, TextSelection selection, Tim | |||
| 		if (good) { | ||||
| 			good->load({}); | ||||
| 		} | ||||
| 		p.drawPixmap(rthumb.topLeft(), _data->thumb->pixBlurredSingle(_realParent->fullId(), _thumbw, _thumbh, paintw, painth, roundRadius, roundCorners)); | ||||
| 		if (const auto normal = _data->thumbnail()) { | ||||
| 			const auto use = (normal->loaded() || !_data->thumbnailInline()) | ||||
| 				? normal | ||||
| 				: _data->thumbnailInline(); | ||||
| 			p.drawPixmap(rthumb.topLeft(), use->pixBlurredSingle(_realParent->fullId(), _thumbw, _thumbh, paintw, painth, roundRadius, roundCorners)); | ||||
| 		} | ||||
| 	} | ||||
| 	if (selected) { | ||||
| 		App::complexOverlayRect(p, rthumb, roundRadius, roundCorners); | ||||
|  | @ -288,13 +310,7 @@ TextState HistoryVideo::textState(QPoint point, StateRequest request) const { | |||
| } | ||||
| 
 | ||||
| QSize HistoryVideo::sizeForGrouping() const { | ||||
| 	const auto width = _data->dimensions.isEmpty() | ||||
| 		? _data->thumb->width() | ||||
| 		: _data->dimensions.width(); | ||||
| 	const auto height = _data->dimensions.isEmpty() | ||||
| 		? _data->thumb->height() | ||||
| 		: _data->dimensions.height(); | ||||
| 	return { std::max(width, 1), std::max(height, 1) }; | ||||
| 	return sizeForAspectRatio(); | ||||
| } | ||||
| 
 | ||||
| void HistoryVideo::drawGrouped( | ||||
|  | @ -384,7 +400,6 @@ void HistoryVideo::drawGrouped( | |||
| 	p.setOpacity(backOpacity); | ||||
| 	if (icon) { | ||||
| 		if (previous && radialOpacity > 0. && radialOpacity < 1.) { | ||||
| 			LOG(("INTERPOLATING: %1").arg(radialOpacity)); | ||||
| 			PaintInterpolatedIcon(p, *icon, *previous, radialOpacity, inner); | ||||
| 		} else { | ||||
| 			icon->paintInCenter(p, inner); | ||||
|  | @ -442,13 +457,18 @@ void HistoryVideo::validateGroupedCache( | |||
| 	using Option = Images::Option; | ||||
| 	const auto good = _data->goodThumbnail(); | ||||
| 	const auto useGood = (good && good->loaded()); | ||||
| 	const auto image = useGood ? good : _data->thumb.get(); | ||||
| 	const auto thumb = _data->thumbnail(); | ||||
| 	const auto useThumb = (thumb && thumb->loaded()); | ||||
| 	const auto image = useGood | ||||
| 		? good | ||||
| 		: useThumb | ||||
| 		? thumb | ||||
| 		: _data->thumbnailInline(); | ||||
| 	if (good && !useGood) { | ||||
| 		good->load({}); | ||||
| 	} | ||||
| 
 | ||||
| 	const auto loaded = useGood ? true : _data->thumb->loaded(); | ||||
| 	const auto loadLevel = loaded ? 1 : 0; | ||||
| 	const auto loadLevel = useGood ? 3 : useThumb ? 2 : image ? 1 : 0; | ||||
| 	const auto width = geometry.width(); | ||||
| 	const auto height = geometry.height(); | ||||
| 	const auto options = Option::Smooth | ||||
|  | @ -466,8 +486,9 @@ void HistoryVideo::validateGroupedCache( | |||
| 		return; | ||||
| 	} | ||||
| 
 | ||||
| 	const auto originalWidth = ConvertScale(_data->thumb->width()); | ||||
| 	const auto originalHeight = ConvertScale(_data->thumb->height()); | ||||
| 	const auto original = sizeForAspectRatio(); | ||||
| 	const auto originalWidth = ConvertScale(original.width()); | ||||
| 	const auto originalHeight = ConvertScale(original.height()); | ||||
| 	const auto pixSize = Ui::GetImageScaleSizeForGeometry( | ||||
| 		{ originalWidth, originalHeight }, | ||||
| 		{ width, height }); | ||||
|  | @ -475,7 +496,7 @@ void HistoryVideo::validateGroupedCache( | |||
| 	const auto pixHeight = pixSize.height() * cIntRetinaFactor(); | ||||
| 
 | ||||
| 	*cacheKey = key; | ||||
| 	*cache = image->pixNoCache(_realParent->fullId(), pixWidth, pixHeight, options, width, height); | ||||
| 	*cache = (image ? image : Image::Blank().get())->pixNoCache(_realParent->fullId(), pixWidth, pixHeight, options, width, height); | ||||
| } | ||||
| 
 | ||||
| void HistoryVideo::setStatusSize(int newSize) const { | ||||
|  |  | |||
|  | @ -83,6 +83,7 @@ private: | |||
| 		not_null<QPixmap*> cache) const; | ||||
| 	void setStatusSize(int newSize) const; | ||||
| 	void updateStatusText() const; | ||||
| 	QSize sizeForAspectRatio() const; | ||||
| 
 | ||||
| 	not_null<DocumentData*> _data; | ||||
| 	int _thumbw = 1; | ||||
|  |  | |||
|  | @ -34,14 +34,16 @@ namespace { | |||
| 
 | ||||
| constexpr auto kMaxOriginalEntryLines = 8192; | ||||
| 
 | ||||
| int articleThumbWidth(PhotoData *thumb, int height) { | ||||
| 	auto w = thumb->medium->width(); | ||||
| 	auto h = thumb->medium->height(); | ||||
| 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); | ||||
| } | ||||
| 
 | ||||
| int articleThumbHeight(PhotoData *thumb, int width) { | ||||
| 	return qMax(thumb->medium->height() * width / thumb->medium->width(), 1); | ||||
| int articleThumbHeight(not_null<PhotoData*> thumb, int width) { | ||||
| 	return qMax( | ||||
| 		thumb->thumbnail()->height() * width / thumb->thumbnail()->width(), | ||||
| 		1); | ||||
| } | ||||
| 
 | ||||
| std::vector<std::unique_ptr<Data::Media>> PrepareCollageMedia( | ||||
|  | @ -410,22 +412,25 @@ void HistoryWebPage::draw(Painter &p, const QRect &r, TextSelection selection, T | |||
| 	auto lineHeight = unitedLineHeight(); | ||||
| 	if (_asArticle) { | ||||
| 		const auto contextId = _parent->data()->fullId(); | ||||
| 		_data->photo->medium->load(contextId, false, false); | ||||
| 		bool full = _data->photo->medium->loaded(); | ||||
| 		_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); | ||||
| 		auto maxw = ConvertScale(_data->photo->medium->width()), maxh = ConvertScale(_data->photo->medium->height()); | ||||
| 		const auto maxw = ConvertScale(_data->photo->thumbnail()->width()); | ||||
| 		const auto maxh = ConvertScale(_data->photo->thumbnail()->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->medium->pixSingle(contextId, pixw, pixh, pw, ph, ImageRoundRadius::Small); | ||||
| 		} else { | ||||
| 			pix = _data->photo->thumb->pixBlurredSingle(contextId, pixw, pixh, pw, ph, ImageRoundRadius::Small); | ||||
| 			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()) { | ||||
| 			pix = blurred->pixBlurredSingle(contextId, pixw, pixh, pw, ph, ImageRoundRadius::Small); | ||||
| 		} | ||||
| 		p.drawPixmapLeft(padding.left() + paintw - pw, tshift, width(), pix); | ||||
| 		if (selected) { | ||||
|  |  | |||
|  | @ -49,7 +49,7 @@ void AddToggleGroupingAction( | |||
| } | ||||
| 
 | ||||
| void SavePhotoToFile(not_null<PhotoData*> photo) { | ||||
| 	if (!photo->date || !photo->loaded()) { | ||||
| 	if (photo->isNull() || !photo->loaded()) { | ||||
| 		return; | ||||
| 	} | ||||
| 
 | ||||
|  | @ -60,17 +60,17 @@ void SavePhotoToFile(not_null<PhotoData*> photo) { | |||
| 		filedialogDefaultName(qsl("photo"), qsl(".jpg")), | ||||
| 		crl::guard(&Auth(), [=](const QString &result) { | ||||
| 			if (!result.isEmpty()) { | ||||
| 				photo->full->pix(Data::FileOrigin()).toImage().save(result, "JPG"); | ||||
| 				photo->large()->original().save(result, "JPG"); | ||||
| 			} | ||||
| 		})); | ||||
| } | ||||
| 
 | ||||
| void CopyImage(not_null<PhotoData*> photo) { | ||||
| 	if (!photo->date || !photo->loaded()) { | ||||
| 	if (photo->isNull() || !photo->loaded()) { | ||||
| 		return; | ||||
| 	} | ||||
| 
 | ||||
| 	QApplication::clipboard()->setPixmap(photo->full->pix(Data::FileOrigin())); | ||||
| 	QApplication::clipboard()->setImage(photo->large()->original()); | ||||
| } | ||||
| 
 | ||||
| void ShowStickerPackInfo(not_null<DocumentData*> document) { | ||||
|  |  | |||
|  | @ -48,23 +48,25 @@ DocumentData *FileBase::getShownDocument() const { | |||
| } | ||||
| 
 | ||||
| int FileBase::content_width() const { | ||||
| 	DocumentData *document = getShownDocument(); | ||||
| 	if (document->dimensions.width() > 0) { | ||||
| 		return document->dimensions.width(); | ||||
| 	} | ||||
| 	if (!document->thumb->isNull()) { | ||||
| 		return ConvertScale(document->thumb->width()); | ||||
| 	if (const auto document = getShownDocument()) { | ||||
| 		if (document->dimensions.width() > 0) { | ||||
| 			return document->dimensions.width(); | ||||
| 		} | ||||
| 		if (const auto thumb = document->thumbnail()) { | ||||
| 			return ConvertScale(thumb->width()); | ||||
| 		} | ||||
| 	} | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| int FileBase::content_height() const { | ||||
| 	DocumentData *document = getShownDocument(); | ||||
| 	if (document->dimensions.height() > 0) { | ||||
| 		return document->dimensions.height(); | ||||
| 	} | ||||
| 	if (!document->thumb->isNull()) { | ||||
| 		return ConvertScale(document->thumb->height()); | ||||
| 	if (const auto document = getShownDocument()) { | ||||
| 		if (document->dimensions.height() > 0) { | ||||
| 			return document->dimensions.height(); | ||||
| 		} | ||||
| 		if (const auto thumb = document->thumbnail()) { | ||||
| 			return ConvertScale(thumb->height()); | ||||
| 		} | ||||
| 	} | ||||
| 	return 0; | ||||
| } | ||||
|  | @ -82,10 +84,10 @@ int FileBase::content_duration() const { | |||
| 	return getResultDuration(); | ||||
| } | ||||
| 
 | ||||
| ImagePtr FileBase::content_thumb() const { | ||||
| 	if (DocumentData *document = getShownDocument()) { | ||||
| 		if (!document->thumb->isNull()) { | ||||
| 			return document->thumb; | ||||
| Image *FileBase::content_thumb() const { | ||||
| 	if (const auto document = getShownDocument()) { | ||||
| 		if (const auto thumb = document->thumbnail()) { | ||||
| 			return thumb; | ||||
| 		} | ||||
| 	} | ||||
| 	return getResultThumb(); | ||||
|  | @ -166,7 +168,7 @@ void Gif::paint(Painter &p, const QRect &clip, const PaintContext *context) cons | |||
| 		auto pixmap = _gif->current(frame.width(), frame.height(), _width, height, ImageRoundRadius::None, RectPart::None, context->paused ? 0 : context->ms); | ||||
| 		p.drawPixmap(r.topLeft(), pixmap); | ||||
| 	} else { | ||||
| 		prepareThumb(_width, height, frame); | ||||
| 		prepareThumbnail({ _width, height }, frame); | ||||
| 		if (_thumb.isNull()) { | ||||
| 			p.fillRect(r, st::overviewPhotoBg); | ||||
| 		} else { | ||||
|  | @ -287,29 +289,37 @@ QSize Gif::countFrameSize() const { | |||
| 	return QSize(framew, frameh); | ||||
| } | ||||
| 
 | ||||
| void Gif::prepareThumb(int32 width, int32 height, const QSize &frame) const { | ||||
| 	const auto origin = fileOrigin(); | ||||
| void Gif::validateThumbnail( | ||||
| 		Image *image, | ||||
| 		QSize size, | ||||
| 		QSize frame, | ||||
| 		bool good) const { | ||||
| 	if (!image || (_thumbGood && !good)) { | ||||
| 		return; | ||||
| 	} else if (!image->loaded()) { | ||||
| 		image->load(fileOrigin()); | ||||
| 		return; | ||||
| 	} else if ((_thumb.size() == size * cIntRetinaFactor()) | ||||
| 		&& (_thumbGood || !good)) { | ||||
| 		return; | ||||
| 	} | ||||
| 	_thumbGood = good; | ||||
| 	_thumb = image->pixNoCache( | ||||
| 		fileOrigin(), | ||||
| 		frame.width() * cIntRetinaFactor(), | ||||
| 		frame.height() * cIntRetinaFactor(), | ||||
| 		(Images::Option::Smooth | ||||
| 			| (good ? Images::Option::None : Images::Option::Blurred)), | ||||
| 		size.width(), | ||||
| 		size.height()); | ||||
| } | ||||
| 
 | ||||
| void Gif::prepareThumbnail(QSize size, QSize frame) const { | ||||
| 	if (const auto document = getShownDocument()) { | ||||
| 		if (!document->thumb->isNull()) { | ||||
| 			if (document->thumb->loaded()) { | ||||
| 				if (_thumb.width() != width * cIntRetinaFactor() || _thumb.height() != height * cIntRetinaFactor()) { | ||||
| 					_thumb = document->thumb->pixNoCache(origin, frame.width() * cIntRetinaFactor(), frame.height() * cIntRetinaFactor(), Images::Option::Smooth, width, height); | ||||
| 				} | ||||
| 			} else { | ||||
| 				document->thumb->load(origin); | ||||
| 			} | ||||
| 		} | ||||
| 		validateThumbnail(document->thumbnail(), size, frame, true); | ||||
| 		validateThumbnail(document->thumbnailInline(), size, frame, false); | ||||
| 	} else { | ||||
| 		const auto thumb = getResultThumb(); | ||||
| 		if (!thumb->isNull()) { | ||||
| 			if (thumb->loaded()) { | ||||
| 				if (_thumb.width() != width * cIntRetinaFactor() || _thumb.height() != height * cIntRetinaFactor()) { | ||||
| 					_thumb = thumb->pixNoCache(origin, frame.width() * cIntRetinaFactor(), frame.height() * cIntRetinaFactor(), Images::Option::Smooth, width, height); | ||||
| 				} | ||||
| 			} else { | ||||
| 				thumb->load(origin); | ||||
| 			} | ||||
| 		} | ||||
| 		validateThumbnail(getResultThumb(), size, frame, true); | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
|  | @ -386,7 +396,7 @@ void Sticker::initDimensions() { | |||
| 
 | ||||
| void Sticker::preload() const { | ||||
| 	if (const auto document = getShownDocument()) { | ||||
| 		document->checkStickerThumb(); | ||||
| 		document->checkStickerSmall(); | ||||
| 	} else if (const auto thumb = getResultThumb()) { | ||||
| 		thumb->load(fileOrigin()); | ||||
| 	} | ||||
|  | @ -402,7 +412,7 @@ void Sticker::paint(Painter &p, const QRect &clip, const PaintContext *context) | |||
| 		p.setOpacity(1); | ||||
| 	} | ||||
| 
 | ||||
| 	prepareThumb(); | ||||
| 	prepareThumbnail(); | ||||
| 	if (!_thumb.isNull()) { | ||||
| 		int w = _thumb.width() / cIntRetinaFactor(), h = _thumb.height() / cIntRetinaFactor(); | ||||
| 		QPoint pos = QPoint((st::stickerPanSize.width() - w) / 2, (st::stickerPanSize.height() - h) / 2); | ||||
|  | @ -442,10 +452,10 @@ QSize Sticker::getThumbSize() const { | |||
| 	return QSize(qMax(w, 1), qMax(h, 1)); | ||||
| } | ||||
| 
 | ||||
| void Sticker::prepareThumb() const { | ||||
| void Sticker::prepareThumbnail() const { | ||||
| 	if (const auto document = getShownDocument()) { | ||||
| 		document->checkStickerThumb(); | ||||
| 		if (const auto sticker = document->getStickerThumb()) { | ||||
| 		document->checkStickerSmall(); | ||||
| 		if (const auto sticker = document->getStickerSmall()) { | ||||
| 			if (!_thumbLoaded && sticker->loaded()) { | ||||
| 				const auto thumbSize = getThumbSize(); | ||||
| 				_thumb = sticker->pix( | ||||
|  | @ -457,18 +467,19 @@ void Sticker::prepareThumb() const { | |||
| 		} | ||||
| 	} else { | ||||
| 		const auto origin = fileOrigin(); | ||||
| 		const auto thumb = getResultThumb(); | ||||
| 		if (thumb->loaded()) { | ||||
| 			if (!_thumbLoaded) { | ||||
| 				const auto thumbSize = getThumbSize(); | ||||
| 				_thumb = thumb->pix( | ||||
| 					origin, | ||||
| 					thumbSize.width(), | ||||
| 					thumbSize.height()); | ||||
| 				_thumbLoaded = true; | ||||
| 		if (const auto thumb = getResultThumb()) { | ||||
| 			if (thumb->loaded()) { | ||||
| 				if (!_thumbLoaded) { | ||||
| 					const auto thumbSize = getThumbSize(); | ||||
| 					_thumb = thumb->pix( | ||||
| 						origin, | ||||
| 						thumbSize.width(), | ||||
| 						thumbSize.height()); | ||||
| 					_thumbLoaded = true; | ||||
| 				} | ||||
| 			} else { | ||||
| 				thumb->load(origin); | ||||
| 			} | ||||
| 		} else { | ||||
| 			thumb->load(origin); | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|  | @ -478,8 +489,8 @@ Photo::Photo(not_null<Context*> context, Result *result) | |||
| } | ||||
| 
 | ||||
| void Photo::initDimensions() { | ||||
| 	PhotoData *photo = getShownPhoto(); | ||||
| 	int32 w = photo->full->width(), h = photo->full->height(); | ||||
| 	const auto photo = getShownPhoto(); | ||||
| 	int32 w = photo->width(), h = photo->height(); | ||||
| 	if (w <= 0 || h <= 0) { | ||||
| 		_maxw = 0; | ||||
| 	} else { | ||||
|  | @ -495,7 +506,7 @@ void Photo::paint(Painter &p, const QRect &clip, const PaintContext *context) co | |||
| 
 | ||||
| 	QRect r(0, 0, _width, height); | ||||
| 
 | ||||
| 	prepareThumb(_width, height, frame); | ||||
| 	prepareThumbnail({ _width, height }, frame); | ||||
| 	if (_thumb.isNull()) { | ||||
| 		p.fillRect(r, st::overviewPhotoBg); | ||||
| 	} else { | ||||
|  | @ -521,7 +532,7 @@ PhotoData *Photo::getShownPhoto() const { | |||
| 
 | ||||
| QSize Photo::countFrameSize() const { | ||||
| 	PhotoData *photo = getShownPhoto(); | ||||
| 	int32 framew = photo->full->width(), frameh = photo->full->height(), height = st::inlineMediaHeight; | ||||
| 	int32 framew = photo->width(), frameh = photo->height(), height = st::inlineMediaHeight; | ||||
| 	if (framew * height > frameh * _width) { | ||||
| 		if (framew < st::maxStickerSize || frameh > height) { | ||||
| 			if (frameh > height || (framew * height / frameh) <= st::maxStickerSize) { | ||||
|  | @ -546,31 +557,38 @@ QSize Photo::countFrameSize() const { | |||
| 	return QSize(framew, frameh); | ||||
| } | ||||
| 
 | ||||
| void Photo::prepareThumb(int32 width, int32 height, const QSize &frame) const { | ||||
| void Photo::validateThumbnail( | ||||
| 		Image *image, | ||||
| 		QSize size, | ||||
| 		QSize frame, | ||||
| 		bool good) const { | ||||
| 	if (!image || (_thumbGood && !good)) { | ||||
| 		return; | ||||
| 	} else if (!image->loaded()) { | ||||
| 		image->load(fileOrigin()); | ||||
| 		return; | ||||
| 	} else if ((_thumb.size() == size * cIntRetinaFactor()) | ||||
| 		&& (_thumbGood || !good)) { | ||||
| 		return; | ||||
| 	} | ||||
| 	const auto origin = fileOrigin(); | ||||
| 	_thumb = image->pixNoCache( | ||||
| 		origin, | ||||
| 		frame.width() * cIntRetinaFactor(), | ||||
| 		frame.height() * cIntRetinaFactor(), | ||||
| 		Images::Option::Smooth | (good ? Images::Option(0) : Images::Option::Blurred), | ||||
| 		size.width(), | ||||
| 		size.height()); | ||||
| 	_thumbGood = good; | ||||
| } | ||||
| 
 | ||||
| void Photo::prepareThumbnail(QSize size, QSize frame) const { | ||||
| 	if (const auto photo = getShownPhoto()) { | ||||
| 		if (photo->medium->loaded()) { | ||||
| 			if (!_thumbLoaded || _thumb.width() != width * cIntRetinaFactor() || _thumb.height() != height * cIntRetinaFactor()) { | ||||
| 				_thumb = photo->medium->pixNoCache(origin, frame.width() * cIntRetinaFactor(), frame.height() * cIntRetinaFactor(), Images::Option::Smooth, width, height); | ||||
| 			} | ||||
| 			_thumbLoaded = true; | ||||
| 		} else { | ||||
| 			if (photo->thumb->loaded()) { | ||||
| 				if (_thumb.width() != width * cIntRetinaFactor() || _thumb.height() != height * cIntRetinaFactor()) { | ||||
| 					_thumb = photo->thumb->pixNoCache(origin, frame.width() * cIntRetinaFactor(), frame.height() * cIntRetinaFactor(), Images::Option::Smooth, width, height); | ||||
| 				} | ||||
| 			} | ||||
| 			photo->medium->load(origin); | ||||
| 		} | ||||
| 	} else { | ||||
| 		const auto thumb = getResultThumb(); | ||||
| 		if (thumb->loaded()) { | ||||
| 			if (_thumb.width() != width * cIntRetinaFactor() || _thumb.height() != height * cIntRetinaFactor()) { | ||||
| 				_thumb = thumb->pixNoCache(origin, frame.width() * cIntRetinaFactor(), frame.height() * cIntRetinaFactor(), Images::Option::Smooth, width, height); | ||||
| 			} | ||||
| 		} else { | ||||
| 			thumb->load(origin); | ||||
| 		} | ||||
| 		validateThumbnail(photo->thumbnail(), size, frame, true); | ||||
| 		validateThumbnail(photo->thumbnailSmall(), size, frame, false); | ||||
| 		validateThumbnail(photo->thumbnailInline(), size, frame, false); | ||||
| 	} else if (const auto thumbnail = getResultThumb()) { | ||||
| 		validateThumbnail(thumbnail, size, frame, true); | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
|  | @ -585,7 +603,7 @@ Video::Video(not_null<Context*> context, Result *result) : FileBase(context, res | |||
| } | ||||
| 
 | ||||
| void Video::initDimensions() { | ||||
| 	bool withThumb = !content_thumb()->isNull(); | ||||
| 	const auto withThumb = (content_thumb() != nullptr); | ||||
| 
 | ||||
| 	_maxw = st::emojiPanWidth - st::emojiScroll.width - st::inlineResultsLeft; | ||||
| 	int32 textWidth = _maxw - (withThumb ? (st::inlineThumbSize + st::inlineThumbSkip) : 0); | ||||
|  | @ -614,9 +632,9 @@ void Video::initDimensions() { | |||
| void Video::paint(Painter &p, const QRect &clip, const PaintContext *context) const { | ||||
| 	int left = st::inlineThumbSize + st::inlineThumbSkip; | ||||
| 
 | ||||
| 	bool withThumb = !content_thumb()->isNull(); | ||||
| 	const auto withThumb = (content_thumb() != nullptr); | ||||
| 	if (withThumb) { | ||||
| 		prepareThumb(st::inlineThumbSize, st::inlineThumbSize); | ||||
| 		prepareThumbnail({ st::inlineThumbSize, st::inlineThumbSize }); | ||||
| 		if (_thumb.isNull()) { | ||||
| 			p.fillRect(rtlrect(0, st::inlineRowMargin, st::inlineThumbSize, st::inlineThumbSize, _width), st::overviewPhotoBg); | ||||
| 		} else { | ||||
|  | @ -661,11 +679,15 @@ TextState Video::getState( | |||
| 	return {}; | ||||
| } | ||||
| 
 | ||||
| void Video::prepareThumb(int32 width, int32 height) const { | ||||
| void Video::prepareThumbnail(QSize size) const { | ||||
| 	Expects(content_thumb() != nullptr); | ||||
| 
 | ||||
| 	const auto thumb = content_thumb(); | ||||
| 	const auto origin = fileOrigin(); | ||||
| 	if (thumb->loaded()) { | ||||
| 		if (_thumb.width() != width * cIntRetinaFactor() || _thumb.height() != height * cIntRetinaFactor()) { | ||||
| 		if (_thumb.size() != size * cIntRetinaFactor()) { | ||||
| 			const auto width = size.width(); | ||||
| 			const auto height = size.height(); | ||||
| 			int32 w = qMax(ConvertScale(thumb->width()), 1), h = qMax(ConvertScale(thumb->height()), 1); | ||||
| 			if (w * height > h * width) { | ||||
| 				if (height < h) { | ||||
|  | @ -948,7 +970,7 @@ void Contact::paint(Painter &p, const QRect &clip, const PaintContext *context) | |||
| 	int32 left = st::emojiPanHeaderLeft - st::inlineResultsLeft; | ||||
| 
 | ||||
| 	left = st::msgFileSize + st::inlineThumbSkip; | ||||
| 	prepareThumb(st::msgFileSize, st::msgFileSize); | ||||
| 	prepareThumbnail(st::msgFileSize, st::msgFileSize); | ||||
| 	QRect rthumb(rtlrect(0, st::inlineRowMargin, st::msgFileSize, st::msgFileSize, _width)); | ||||
| 	p.drawPixmapLeft(rthumb.topLeft(), _width, _thumb); | ||||
| 
 | ||||
|  | @ -978,9 +1000,9 @@ TextState Contact::getState( | |||
| 	return {}; | ||||
| } | ||||
| 
 | ||||
| void Contact::prepareThumb(int width, int height) const { | ||||
| void Contact::prepareThumbnail(int width, int height) const { | ||||
| 	const auto thumb = getResultThumb(); | ||||
| 	if (thumb->isNull()) { | ||||
| 	if (!thumb) { | ||||
| 		if (_thumb.width() != width * cIntRetinaFactor() || _thumb.height() != height * cIntRetinaFactor()) { | ||||
| 			_thumb = getResultContactAvatar(width, height); | ||||
| 		} | ||||
|  | @ -1059,11 +1081,11 @@ void Article::paint(Painter &p, const QRect &clip, const PaintContext *context) | |||
| 	int32 left = st::emojiPanHeaderLeft - st::inlineResultsLeft; | ||||
| 	if (_withThumb) { | ||||
| 		left = st::inlineThumbSize + st::inlineThumbSkip; | ||||
| 		prepareThumb(st::inlineThumbSize, st::inlineThumbSize); | ||||
| 		prepareThumbnail(st::inlineThumbSize, st::inlineThumbSize); | ||||
| 		QRect rthumb(rtlrect(0, st::inlineRowMargin, st::inlineThumbSize, st::inlineThumbSize, _width)); | ||||
| 		if (_thumb.isNull()) { | ||||
| 			ImagePtr thumb = getResultThumb(); | ||||
| 			if (thumb->isNull() && !_thumbLetter.isEmpty()) { | ||||
| 			const auto thumb = getResultThumb(); | ||||
| 			if (!thumb && !_thumbLetter.isEmpty()) { | ||||
| 				int32 index = (_thumbLetter.at(0).unicode() % 4); | ||||
| 				style::color colors[] = { | ||||
| 					st::msgFile3Bg, | ||||
|  | @ -1126,9 +1148,9 @@ TextState Article::getState( | |||
| 	return {}; | ||||
| } | ||||
| 
 | ||||
| void Article::prepareThumb(int width, int height) const { | ||||
| void Article::prepareThumbnail(int width, int height) const { | ||||
| 	const auto thumb = getResultThumb(); | ||||
| 	if (thumb->isNull()) { | ||||
| 	if (!thumb) { | ||||
| 		if (_thumb.width() != width * cIntRetinaFactor() || _thumb.height() != height * cIntRetinaFactor()) { | ||||
| 			_thumb = getResultContactAvatar(width, height); | ||||
| 		} | ||||
|  | @ -1258,7 +1280,7 @@ void Game::paint(Painter &p, const QRect &clip, const PaintContext *context) con | |||
| 	} | ||||
| 
 | ||||
| 	if (!thumbDisplayed) { | ||||
| 		prepareThumb(st::inlineThumbSize, st::inlineThumbSize); | ||||
| 		prepareThumbnail({ st::inlineThumbSize, st::inlineThumbSize }); | ||||
| 		if (_thumb.isNull()) { | ||||
| 			p.fillRect(rthumb, st::overviewPhotoBg); | ||||
| 		} else { | ||||
|  | @ -1302,41 +1324,52 @@ TextState Game::getState( | |||
| 	return {}; | ||||
| } | ||||
| 
 | ||||
| void Game::prepareThumb(int width, int height) const { | ||||
| 	const auto thumb = [this] { | ||||
| 		if (auto photo = getResultPhoto()) { | ||||
| 			return photo->medium; | ||||
| 		} else if (auto document = getResultDocument()) { | ||||
| 			return document->thumb; | ||||
| 		} | ||||
| 		return ImagePtr(); | ||||
| 	}(); | ||||
| 	if (thumb->isNull()) { | ||||
| 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()) { | ||||
| 		validateThumbnail(document->thumbnail(), size, true); | ||||
| 		validateThumbnail(document->thumbnailInline(), size, false); | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| void Game::validateThumbnail(Image *image, QSize size, bool good) const { | ||||
| 	if (!image || (_thumbGood && !good)) { | ||||
| 		return; | ||||
| 	} else if (!image->loaded()) { | ||||
| 		image->load(fileOrigin()); | ||||
| 		return; | ||||
| 	} else if ((_thumb.size() == size * cIntRetinaFactor()) | ||||
| 		&& (_thumbGood || !good)) { | ||||
| 		return; | ||||
| 	} | ||||
| 
 | ||||
| 	const auto origin = fileOrigin(); | ||||
| 	if (thumb->loaded()) { | ||||
| 		if (_thumb.width() != width * cIntRetinaFactor() || _thumb.height() != height * cIntRetinaFactor()) { | ||||
| 			int w = qMax(ConvertScale(thumb->width()), 1), h = qMax(ConvertScale(thumb->height()), 1); | ||||
| 			auto resizeByHeight1 = (w * height > h * width) && (h >= height); | ||||
| 			auto resizeByHeight2 = (h * width >= w * height) && (w < width); | ||||
| 			if (resizeByHeight1 || resizeByHeight2) { | ||||
| 				if (h > height) { | ||||
| 					w = w * height / h; | ||||
| 					h = height; | ||||
| 				} | ||||
| 			} else { | ||||
| 				if (w > width) { | ||||
| 					h = h * width / w; | ||||
| 					w = width; | ||||
| 				} | ||||
| 			} | ||||
| 			_thumb = thumb->pixNoCache(origin, w * cIntRetinaFactor(), h * cIntRetinaFactor(), Images::Option::Smooth, width, height); | ||||
| 	const auto width = size.width(); | ||||
| 	const auto height = size.height(); | ||||
| 	auto w = qMax(ConvertScale(image->width()), 1); | ||||
| 	auto h = qMax(ConvertScale(image->height()), 1); | ||||
| 	auto resizeByHeight1 = (w * height > h * width) && (h >= height); | ||||
| 	auto resizeByHeight2 = (h * width >= w * height) && (w < width); | ||||
| 	if (resizeByHeight1 || resizeByHeight2) { | ||||
| 		if (h > height) { | ||||
| 			w = w * height / h; | ||||
| 			h = height; | ||||
| 		} | ||||
| 	} else { | ||||
| 		thumb->load(origin); | ||||
| 		if (w > width) { | ||||
| 			h = h * width / w; | ||||
| 			w = width; | ||||
| 		} | ||||
| 	} | ||||
| 	_thumbGood = good; | ||||
| 	_thumb = image->pixNoCache( | ||||
| 		fileOrigin(), | ||||
| 		w * cIntRetinaFactor(), | ||||
| 		h * cIntRetinaFactor(), | ||||
| 		(Images::Option::Smooth | ||||
| 			| (good ? Images::Option::None : Images::Option::Blurred)), | ||||
| 		size.width(), | ||||
| 		size.height()); | ||||
| } | ||||
| 
 | ||||
| bool Game::isRadialAnimation(TimeMs ms) const { | ||||
|  |  | |||
|  | @ -28,7 +28,7 @@ protected: | |||
| 	int content_width() const; | ||||
| 	int content_height() const; | ||||
| 	int content_duration() const; | ||||
| 	ImagePtr content_thumb() const; | ||||
| 	Image *content_thumb() const; | ||||
| }; | ||||
| 
 | ||||
| class DeleteSavedGifClickHandler : public LeftButtonClickHandler { | ||||
|  | @ -83,7 +83,13 @@ private: | |||
| 	Media::Clip::ReaderPointer _gif; | ||||
| 	ClickHandlerPtr _delete; | ||||
| 	mutable QPixmap _thumb; | ||||
| 	void prepareThumb(int32 width, int32 height, const QSize &frame) const; | ||||
| 	mutable bool _thumbGood = false; | ||||
| 	void validateThumbnail( | ||||
| 		Image *image, | ||||
| 		QSize size, | ||||
| 		QSize frame, | ||||
| 		bool good) const; | ||||
| 	void prepareThumbnail(QSize size, QSize frame) const; | ||||
| 
 | ||||
| 	void ensureAnimation() const; | ||||
| 	bool isRadialAnimation(TimeMs ms) const; | ||||
|  | @ -131,8 +137,13 @@ private: | |||
| 	QSize countFrameSize() const; | ||||
| 
 | ||||
| 	mutable QPixmap _thumb; | ||||
| 	mutable bool _thumbLoaded = false; | ||||
| 	void prepareThumb(int32 width, int32 height, const QSize &frame) const; | ||||
| 	mutable bool _thumbGood = false; | ||||
| 	void prepareThumbnail(QSize size, QSize frame) const; | ||||
| 	void validateThumbnail( | ||||
| 		Image *image, | ||||
| 		QSize size, | ||||
| 		QSize frame, | ||||
| 		bool good) const; | ||||
| 
 | ||||
| }; | ||||
| 
 | ||||
|  | @ -168,7 +179,7 @@ private: | |||
| 
 | ||||
| 	mutable QPixmap _thumb; | ||||
| 	mutable bool _thumbLoaded = false; | ||||
| 	void prepareThumb() const; | ||||
| 	void prepareThumbnail() const; | ||||
| 
 | ||||
| }; | ||||
| 
 | ||||
|  | @ -191,7 +202,7 @@ private: | |||
| 	QString _duration; | ||||
| 	int _durationWidth = 0; | ||||
| 
 | ||||
| 	void prepareThumb(int32 width, int32 height) const; | ||||
| 	void prepareThumbnail(QSize size) const; | ||||
| 
 | ||||
| }; | ||||
| 
 | ||||
|  | @ -302,7 +313,7 @@ private: | |||
| 	mutable QPixmap _thumb; | ||||
| 	Text _title, _description; | ||||
| 
 | ||||
| 	void prepareThumb(int width, int height) const; | ||||
| 	void prepareThumbnail(int width, int height) const; | ||||
| 
 | ||||
| }; | ||||
| 
 | ||||
|  | @ -327,7 +338,7 @@ private: | |||
| 	QString _thumbLetter, _urlText; | ||||
| 	int32 _urlWidth; | ||||
| 
 | ||||
| 	void prepareThumb(int width, int height) const; | ||||
| 	void prepareThumbnail(int width, int height) const; | ||||
| 
 | ||||
| }; | ||||
| 
 | ||||
|  | @ -346,7 +357,8 @@ public: | |||
| private: | ||||
| 	void countFrameSize(); | ||||
| 
 | ||||
| 	void prepareThumb(int32 width, int32 height) const; | ||||
| 	void prepareThumbnail(QSize size) const; | ||||
| 	void validateThumbnail(Image *image, QSize size, bool good) const; | ||||
| 
 | ||||
| 	bool isRadialAnimation(TimeMs ms) const; | ||||
| 	void step_radial(TimeMs ms, bool timer); | ||||
|  | @ -355,6 +367,7 @@ private: | |||
| 
 | ||||
| 	Media::Clip::ReaderPointer _gif; | ||||
| 	mutable QPixmap _thumb; | ||||
| 	mutable bool _thumbGood = false; | ||||
| 	mutable std::unique_ptr<Ui::RadialAnimation> _radial; | ||||
| 	Text _title, _description; | ||||
| 
 | ||||
|  |  | |||
|  | @ -78,16 +78,16 @@ void ItemBase::preload() const { | |||
| 	const auto origin = fileOrigin(); | ||||
| 	if (_result) { | ||||
| 		if (_result->_photo) { | ||||
| 			_result->_photo->thumb->load(origin); | ||||
| 			_result->_photo->loadThumbnail(origin); | ||||
| 		} else if (_result->_document) { | ||||
| 			_result->_document->thumb->load(origin); | ||||
| 			_result->_document->loadThumbnail(origin); | ||||
| 		} else if (!_result->_thumb->isNull()) { | ||||
| 			_result->_thumb->load(origin); | ||||
| 		} | ||||
| 	} else if (_doc) { | ||||
| 		_doc->thumb->load(origin); | ||||
| 		_doc->loadThumbnail(origin); | ||||
| 	} else if (_photo) { | ||||
| 		_photo->medium->load(origin); | ||||
| 		_photo->loadThumbnail(origin); | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
|  | @ -145,17 +145,17 @@ PhotoData *ItemBase::getResultPhoto() const { | |||
| 	return _result ? _result->_photo : nullptr; | ||||
| } | ||||
| 
 | ||||
| ImagePtr ItemBase::getResultThumb() const { | ||||
| Image *ItemBase::getResultThumb() const { | ||||
| 	if (_result) { | ||||
| 		if (_result->_photo && !_result->_photo->thumb->isNull()) { | ||||
| 			return _result->_photo->thumb; | ||||
| 		if (_result->_photo) { | ||||
| 			return _result->_photo->thumbnail(); | ||||
| 		} else if (!_result->_thumb->isNull()) { | ||||
| 			return _result->_thumb.get(); | ||||
| 		} else if (!_result->_locationThumb->isNull()) { | ||||
| 			return _result->_locationThumb.get(); | ||||
| 		} | ||||
| 		if (!_result->_thumb->isNull()) { | ||||
| 			return _result->_thumb; | ||||
| 		} | ||||
| 		return _result->_locationThumb; | ||||
| 	} | ||||
| 	return ImagePtr(); | ||||
| 	return nullptr; | ||||
| } | ||||
| 
 | ||||
| QPixmap ItemBase::getResultContactAvatar(int width, int height) const { | ||||
|  |  | |||
|  | @ -95,7 +95,7 @@ public: | |||
| protected: | ||||
| 	DocumentData *getResultDocument() const; | ||||
| 	PhotoData *getResultPhoto() const; | ||||
| 	ImagePtr getResultThumb() const; | ||||
| 	Image *getResultThumb() const; | ||||
| 	QPixmap getResultContactAvatar(int width, int height) const; | ||||
| 	int getResultDuration() const; | ||||
| 	QString getResultUrl() const; | ||||
|  |  | |||
|  | @ -110,8 +110,8 @@ std::unique_ptr<Result> Result::create(uint64 queryId, const MTPBotInlineResult | |||
| 		message = &r.vsend_message; | ||||
| 	} break; | ||||
| 	} | ||||
| 	auto badAttachment = (result->_photo && result->_photo->full->isNull()) | ||||
| 		|| (result->_document && !result->_document->isValid()); | ||||
| 	auto badAttachment = (result->_photo && result->_photo->isNull()) | ||||
| 		|| (result->_document && result->_document->isNull()); | ||||
| 
 | ||||
| 	if (!message) { | ||||
| 		return nullptr; | ||||
|  | @ -246,11 +246,10 @@ std::unique_ptr<Result> Result::create(uint64 queryId, const MTPBotInlineResult | |||
| 
 | ||||
| bool Result::onChoose(Layout::ItemBase *layout) { | ||||
| 	if (_photo && _type == Type::Photo) { | ||||
| 		if (_photo->medium->loaded() || _photo->thumb->loaded()) { | ||||
| 		if (_photo->thumbnail()->loaded()) { | ||||
| 			return true; | ||||
| 		} 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()); | ||||
| 		} | ||||
| 		return false; | ||||
| 	} | ||||
|  |  | |||
|  | @ -1640,8 +1640,8 @@ void MainWidget::checkChatBackground() { | |||
| 	}); | ||||
| } | ||||
| 
 | ||||
| ImagePtr MainWidget::newBackgroundThumb() { | ||||
| 	return _background ? _background->data.thumb : ImagePtr(); | ||||
| Image *MainWidget::newBackgroundThumb() { | ||||
| 	return _background ? _background->data.thumb : nullptr; | ||||
| } | ||||
| 
 | ||||
| void MainWidget::messageDataReceived(ChannelData *channel, MsgId msgId) { | ||||
|  |  | |||
|  | @ -237,7 +237,7 @@ public: | |||
| 	bool chatBackgroundLoading(); | ||||
| 	float64 chatBackgroundProgress() const; | ||||
| 	void checkChatBackground(); | ||||
| 	ImagePtr newBackgroundThumb(); | ||||
| 	Image *newBackgroundThumb(); | ||||
| 
 | ||||
| 	void messageDataReceived(ChannelData *channel, MsgId msgId); | ||||
| 	void updateBotKeyboard(History *h); | ||||
|  |  | |||
|  | @ -318,7 +318,7 @@ void Instance::play(const AudioMsgId &audioId) { | |||
| 		} | ||||
| 	} | ||||
| 	if (document->isVoiceMessage() || document->isVideoMessage()) { | ||||
| 		document->session()->data().markMediaRead(document); | ||||
| 		document->owner().markMediaRead(document); | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
|  |  | |||
|  | @ -129,7 +129,7 @@ public: | |||
| 
 | ||||
| 	Thumb( | ||||
| 		Key key, | ||||
| 		ImagePtr image, | ||||
| 		Image *image, | ||||
| 		Data::FileOrigin origin, | ||||
| 		Fn<void()> handler); | ||||
| 
 | ||||
|  | @ -157,7 +157,7 @@ private: | |||
| 
 | ||||
| 	ClickHandlerPtr _link; | ||||
| 	const Key _key; | ||||
| 	ImagePtr _image; | ||||
| 	Image *_image = nullptr; | ||||
| 	Data::FileOrigin _origin; | ||||
| 	State _state = State::Alive; | ||||
| 	QPixmap _full; | ||||
|  | @ -172,7 +172,7 @@ private: | |||
| 
 | ||||
| GroupThumbs::Thumb::Thumb( | ||||
| 	Key key, | ||||
| 	ImagePtr image, | ||||
| 	Image *image, | ||||
| 	Data::FileOrigin origin, | ||||
| 	Fn<void()> handler) | ||||
| : _key(key) | ||||
|  | @ -186,15 +186,15 @@ GroupThumbs::Thumb::Thumb( | |||
| } | ||||
| 
 | ||||
| QSize GroupThumbs::Thumb::wantedPixSize() const { | ||||
| 	const auto originalWidth = std::max(_image->width(), 1); | ||||
| 	const auto originalHeight = std::max(_image->height(), 1); | ||||
| 	const auto originalWidth = _image ? std::max(_image->width(), 1) : 1; | ||||
| 	const auto originalHeight = _image ? std::max(_image->height(), 1) : 1; | ||||
| 	const auto pixHeight = st::mediaviewGroupHeight; | ||||
| 	const auto pixWidth = originalWidth * pixHeight / originalHeight; | ||||
| 	return { pixWidth, pixHeight }; | ||||
| } | ||||
| 
 | ||||
| void GroupThumbs::Thumb::validateImage() { | ||||
| 	if (!_full.isNull()) { | ||||
| 	if (!_full.isNull() || !_image) { | ||||
| 		return; | ||||
| 	} | ||||
| 	_image->load(_origin); | ||||
|  | @ -516,20 +516,18 @@ 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->date ? photo->thumb : ImagePtr()); | ||||
| 		return createThumb(key, photo->thumbnail()); | ||||
| 	} else if (const auto msgId = base::get_if<FullMsgId>(&key)) { | ||||
| 		if (const auto item = App::histItemById(*msgId)) { | ||||
| 			if (const auto media = item->media()) { | ||||
| 				if (const auto photo = media->photo()) { | ||||
| 					return createThumb(key, photo->thumb); | ||||
| 					return createThumb(key, photo->thumbnail()); | ||||
| 				} else if (const auto document = media->document()) { | ||||
| 					return createThumb(key, document->thumb); | ||||
| 					return createThumb(key, document->thumbnail()); | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 		return createThumb(key, ImagePtr()); | ||||
| 		return createThumb(key, nullptr); | ||||
| 	} else if (const auto collageKey = base::get_if<CollageKey>(&key)) { | ||||
| 		if (const auto itemId = base::get_if<FullMsgId>(&_context)) { | ||||
| 			if (const auto item = App::histItemById(*itemId)) { | ||||
|  | @ -543,7 +541,7 @@ auto GroupThumbs::createThumb(Key key) | |||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 		return createThumb(key, ImagePtr()); | ||||
| 		return createThumb(key, nullptr); | ||||
| 	} | ||||
| 	Unexpected("Value of Key in GroupThumbs::createThumb()"); | ||||
| } | ||||
|  | @ -554,18 +552,18 @@ auto GroupThumbs::createThumb( | |||
| 	int index) | ||||
| -> std::unique_ptr<Thumb> { | ||||
| 	if (index < 0 || index >= collage.items.size()) { | ||||
| 		return createThumb(key, ImagePtr()); | ||||
| 		return createThumb(key, nullptr); | ||||
| 	} | ||||
| 	const auto &item = collage.items[index]; | ||||
| 	if (const auto photo = base::get_if<PhotoData*>(&item)) { | ||||
| 		return createThumb(key, (*photo)->thumb); | ||||
| 		return createThumb(key, (*photo)->thumbnail()); | ||||
| 	} else if (const auto document = base::get_if<DocumentData*>(&item)) { | ||||
| 		return createThumb(key, (*document)->thumb); | ||||
| 		return createThumb(key, (*document)->thumbnail()); | ||||
| 	} | ||||
| 	return createThumb(key, ImagePtr()); | ||||
| 	return createThumb(key, nullptr); | ||||
| } | ||||
| 
 | ||||
| auto GroupThumbs::createThumb(Key key, ImagePtr image) | ||||
| auto GroupThumbs::createThumb(Key key, Image *image) | ||||
| -> std::unique_ptr<Thumb> { | ||||
| 	const auto weak = base::make_weak(this); | ||||
| 	const auto origin = ComputeFileOrigin(key, _context); | ||||
|  |  | |||
|  | @ -99,7 +99,7 @@ private: | |||
| 		Key key, | ||||
| 		const WebPageCollage &collage, | ||||
| 		int index); | ||||
| 	std::unique_ptr<Thumb> createThumb(Key key, ImagePtr image); | ||||
| 	std::unique_ptr<Thumb> createThumb(Key key, Image *image); | ||||
| 
 | ||||
| 	void update(); | ||||
| 	void countUpdatedRect(); | ||||
|  |  | |||
|  | @ -565,7 +565,7 @@ float64 MediaView::radialProgress() const { | |||
| 	if (_doc) { | ||||
| 		return _doc->progress(); | ||||
| 	} else if (_photo) { | ||||
| 		return _photo->full->progress(); | ||||
| 		return _photo->large()->progress(); | ||||
| 	} | ||||
| 	return 1.; | ||||
| } | ||||
|  | @ -574,7 +574,7 @@ bool MediaView::radialLoading() const { | |||
| 	if (_doc) { | ||||
| 		return _doc->loading(); | ||||
| 	} else if (_photo) { | ||||
| 		return _photo->full->loading(); | ||||
| 		return _photo->large()->loading(); | ||||
| 	} | ||||
| 	return false; | ||||
| } | ||||
|  | @ -897,7 +897,7 @@ void MediaView::onSaveAs() { | |||
| 				_photo->date), | ||||
| 			crl::guard(this, [this, photo = _photo](const QString &result) { | ||||
| 				if (!result.isEmpty() && _photo == photo && photo->loaded()) { | ||||
| 					photo->full->pix(fileOrigin()).toImage().save(result, "JPG"); | ||||
| 					photo->large()->original().save(result, "JPG"); | ||||
| 				} | ||||
| 				psShowOverAll(this); | ||||
| 			}), crl::guard(this, [this] { | ||||
|  | @ -1019,7 +1019,7 @@ void MediaView::onDownload() { | |||
| 		} else { | ||||
| 			if (!QDir().exists(path)) QDir().mkpath(path); | ||||
| 			toName = filedialogDefaultName(qsl("photo"), qsl(".jpg"), path); | ||||
| 			if (!_photo->full->pix(fileOrigin()).toImage().save(toName, "JPG")) { | ||||
| 			if (!_photo->large()->original().save(toName, "JPG")) { | ||||
| 				toName = QString(); | ||||
| 			} | ||||
| 		} | ||||
|  | @ -1098,7 +1098,7 @@ void MediaView::onCopy() { | |||
| 	} else { | ||||
| 		if (!_photo || !_photo->loaded()) return; | ||||
| 
 | ||||
| 		QApplication::clipboard()->setPixmap(_photo->full->pix(fileOrigin())); | ||||
| 		QApplication::clipboard()->setPixmap(_photo->large()->pix(fileOrigin())); | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
|  | @ -1538,6 +1538,10 @@ void MediaView::showDocument(not_null<DocumentData*> document, HistoryItem *cont | |||
| } | ||||
| 
 | ||||
| void MediaView::displayPhoto(not_null<PhotoData*> photo, HistoryItem *item) { | ||||
| 	if (photo->isNull()) { | ||||
| 		displayDocument(nullptr, item); | ||||
| 		return; | ||||
| 	} | ||||
| 	stopGif(); | ||||
| 	destroyThemePreview(); | ||||
| 	_doc = _autoplayVideoDocument = nullptr; | ||||
|  | @ -1555,11 +1559,11 @@ void MediaView::displayPhoto(not_null<PhotoData*> photo, HistoryItem *item) { | |||
| 
 | ||||
| 	_zoomToScreen = 0; | ||||
| 	Auth().downloader().clearPriorities(); | ||||
| 	_full = -1; | ||||
| 	_blurred = true; | ||||
| 	_current = QPixmap(); | ||||
| 	_down = OverNone; | ||||
| 	_w = ConvertScale(photo->full->width()); | ||||
| 	_h = ConvertScale(photo->full->height()); | ||||
| 	_w = ConvertScale(photo->width()); | ||||
| 	_h = ConvertScale(photo->height()); | ||||
| 	if (isHidden()) { | ||||
| 		moveToScreen(); | ||||
| 	} | ||||
|  | @ -1618,10 +1622,10 @@ void MediaView::displayDocument(DocumentData *doc, HistoryItem *item) { // empty | |||
| 	} | ||||
| 	if (_doc) { | ||||
| 		if (_doc->sticker()) { | ||||
| 			if (const auto image = _doc->getStickerImage()) { | ||||
| 			if (const auto image = _doc->getStickerLarge()) { | ||||
| 				_current = image->pix(fileOrigin()); | ||||
| 			} else { | ||||
| 				_current = _doc->thumb->pixBlurred( | ||||
| 			} else if (_doc->hasThumbnail()) { | ||||
| 				_current = _doc->thumbnail()->pixBlurred( | ||||
| 					fileOrigin(), | ||||
| 					_doc->dimensions.width(), | ||||
| 					_doc->dimensions.height()); | ||||
|  | @ -1647,7 +1651,7 @@ void MediaView::displayDocument(DocumentData *doc, HistoryItem *item) { // empty | |||
| 
 | ||||
| 	_docIconRect = QRect((width() - st::mediaviewFileIconSize) / 2, (height() - st::mediaviewFileIconSize) / 2, st::mediaviewFileIconSize, st::mediaviewFileIconSize); | ||||
| 	if (fileBubbleShown()) { | ||||
| 		if (!_doc || _doc->thumb->isNull()) { | ||||
| 		if (!_doc || !_doc->hasThumbnail()) { | ||||
| 			int32 colorIndex = documentColorIndex(_doc, _docExt); | ||||
| 			_docIconColor = documentColor(colorIndex); | ||||
| 			const style::icon *(thumbs[]) = { &st::mediaviewFileBlue, &st::mediaviewFileGreen, &st::mediaviewFileRed, &st::mediaviewFileYellow }; | ||||
|  | @ -1660,8 +1664,8 @@ void MediaView::displayDocument(DocumentData *doc, HistoryItem *item) { // empty | |||
| 				_docExtWidth = st::mediaviewFileExtFont->width(_docExt); | ||||
| 			} | ||||
| 		} else { | ||||
| 			_doc->thumb->load(fileOrigin()); | ||||
| 			int32 tw = _doc->thumb->width(), th = _doc->thumb->height(); | ||||
| 			_doc->loadThumbnail(fileOrigin()); | ||||
| 			int32 tw = _doc->thumbnail()->width(), th = _doc->thumbnail()->height(); | ||||
| 			if (!tw || !th) { | ||||
| 				_docThumbx = _docThumby = _docThumbw = 0; | ||||
| 			} else if (tw > th) { | ||||
|  | @ -1745,7 +1749,7 @@ void MediaView::displayDocument(DocumentData *doc, HistoryItem *item) { // empty | |||
| 	} else { | ||||
| 		_from = _user; | ||||
| 	} | ||||
| 	_full = 1; | ||||
| 	_blurred = false; | ||||
| 	displayFinished(); | ||||
| } | ||||
| 
 | ||||
|  | @ -1794,10 +1798,10 @@ void MediaView::initAnimation() { | |||
| 	} else if (_doc->dimensions.width() && _doc->dimensions.height()) { | ||||
| 		auto w = _doc->dimensions.width(); | ||||
| 		auto h = _doc->dimensions.height(); | ||||
| 		_current = _doc->thumb->pixNoCache(fileOrigin(), w, h, VideoThumbOptions(_doc), w / cIntRetinaFactor(), h / cIntRetinaFactor()); | ||||
| 		_current = _doc->thumbnail()->pixNoCache(fileOrigin(), w, h, VideoThumbOptions(_doc), w / cIntRetinaFactor(), h / cIntRetinaFactor()); | ||||
| 		_current.setDevicePixelRatio(cRetinaFactor()); | ||||
| 	} else { | ||||
| 		_current = _doc->thumb->pixNoCache(fileOrigin(), _doc->thumb->width(), _doc->thumb->height(), VideoThumbOptions(_doc), st::mediaviewFileIconSize, st::mediaviewFileIconSize); | ||||
| 		_current = _doc->thumbnail()->pixNoCache(fileOrigin(), _doc->thumbnail()->width(), _doc->thumbnail()->height(), VideoThumbOptions(_doc), st::mediaviewFileIconSize, st::mediaviewFileIconSize); | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
|  | @ -1810,10 +1814,10 @@ void MediaView::createClipReader() { | |||
| 	if (_doc->dimensions.width() && _doc->dimensions.height()) { | ||||
| 		int w = _doc->dimensions.width(); | ||||
| 		int h = _doc->dimensions.height(); | ||||
| 		_current = _doc->thumb->pixNoCache(fileOrigin(), w, h, VideoThumbOptions(_doc), w / cIntRetinaFactor(), h / cIntRetinaFactor()); | ||||
| 		_current = _doc->thumbnail()->pixNoCache(fileOrigin(), w, h, VideoThumbOptions(_doc), w / cIntRetinaFactor(), h / cIntRetinaFactor()); | ||||
| 		_current.setDevicePixelRatio(cRetinaFactor()); | ||||
| 	} else { | ||||
| 		_current = _doc->thumb->pixNoCache(fileOrigin(), _doc->thumb->width(), _doc->thumb->height(), VideoThumbOptions(_doc), st::mediaviewFileIconSize, st::mediaviewFileIconSize); | ||||
| 		_current = _doc->thumbnail()->pixNoCache(fileOrigin(), _doc->thumbnail()->width(), _doc->thumbnail()->height(), VideoThumbOptions(_doc), st::mediaviewFileIconSize, st::mediaviewFileIconSize); | ||||
| 	} | ||||
| 	auto mode = (_doc->isVideoFile() || _doc->isVideoMessage()) | ||||
| 		? Media::Clip::Reader::Mode::Video | ||||
|  | @ -2050,6 +2054,38 @@ void MediaView::updateSilentVideoPlaybackState() { | |||
| 	updateVideoPlaybackState(state); | ||||
| } | ||||
| 
 | ||||
| void MediaView::validatePhotoImage(Image *image, bool blurred) { | ||||
| 	if (!image || !image->loaded()) { | ||||
| 		if (!blurred) { | ||||
| 			image->load(fileOrigin()); | ||||
| 		} | ||||
| 		return; | ||||
| 	} else if (!_current.isNull() && (blurred || !_blurred)) { | ||||
| 		return; | ||||
| 	} | ||||
| 	const auto w = _width * cIntRetinaFactor(); | ||||
| 	const auto h = int((_photo->height() * (qreal(w) / qreal(_photo->width()))) + 0.9999); | ||||
| 	_current = image->pixNoCache( | ||||
| 		fileOrigin(), | ||||
| 		w, | ||||
| 		h, | ||||
| 		Images::Option::Smooth | ||||
| 		| (blurred ? Images::Option::Blurred : Images::Option(0))); | ||||
| 	_current.setDevicePixelRatio(cRetinaFactor()); | ||||
| 	_blurred = blurred; | ||||
| } | ||||
| 
 | ||||
| void MediaView::validatePhotoCurrentImage() { | ||||
| 	validatePhotoImage(_photo->large(), false); | ||||
| 	validatePhotoImage(_photo->thumbnail(), true); | ||||
| 	validatePhotoImage(_photo->thumbnailSmall(), true); | ||||
| 	validatePhotoImage(_photo->thumbnailInline(), true); | ||||
| 	if (_current.isNull()) { | ||||
| 		_photo->loadThumbnailSmall(fileOrigin()); | ||||
| 		validatePhotoImage(Image::Blank().get(), true); | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| void MediaView::paintEvent(QPaintEvent *e) { | ||||
| 	QRect r(e->rect()); | ||||
| 	QRegion region(e->region()); | ||||
|  | @ -2082,39 +2118,24 @@ void MediaView::paintEvent(QPaintEvent *e) { | |||
| 
 | ||||
| 	// photo
 | ||||
| 	if (_photo) { | ||||
| 		int32 w = _width * cIntRetinaFactor(); | ||||
| 		if (_full <= 0 && _photo->loaded()) { | ||||
| 			int32 h = int((_photo->full->height() * (qreal(w) / qreal(_photo->full->width()))) + 0.9999); | ||||
| 			_current = _photo->full->pixNoCache(fileOrigin(), w, h, Images::Option::Smooth); | ||||
| 			_current.setDevicePixelRatio(cRetinaFactor()); | ||||
| 			_full = 1; | ||||
| 		} else if (_full < 0 && _photo->medium->loaded()) { | ||||
| 			int32 h = int((_photo->full->height() * (qreal(w) / qreal(_photo->full->width()))) + 0.9999); | ||||
| 			_current = _photo->medium->pixNoCache(fileOrigin(), w, h, Images::Option::Smooth | Images::Option::Blurred); | ||||
| 			_current.setDevicePixelRatio(cRetinaFactor()); | ||||
| 			_full = 0; | ||||
| 		} else if (_current.isNull() && _photo->thumb->loaded()) { | ||||
| 			int32 h = int((_photo->full->height() * (qreal(w) / qreal(_photo->full->width()))) + 0.9999); | ||||
| 			_current = _photo->thumb->pixNoCache(fileOrigin(), w, h, Images::Option::Smooth | Images::Option::Blurred); | ||||
| 			_current.setDevicePixelRatio(cRetinaFactor()); | ||||
| 		} else if (_current.isNull()) { | ||||
| 			_current = _photo->thumb->pix(fileOrigin()); | ||||
| 		} | ||||
| 		validatePhotoCurrentImage(); | ||||
| 	} | ||||
| 	p.setOpacity(1); | ||||
| 	if (_photo || fileShown()) { | ||||
| 		QRect imgRect(_x, _y, _w, _h); | ||||
| 		if (imgRect.intersects(r)) { | ||||
| 			auto rounding = (_doc && _doc->isVideoMessage()) ? ImageRoundRadius::Ellipse : ImageRoundRadius::None; | ||||
| 			auto toDraw = _current.isNull() ? _gif->current(_gif->width() / cIntRetinaFactor(), _gif->height() / cIntRetinaFactor(), _gif->width() / cIntRetinaFactor(), _gif->height() / cIntRetinaFactor(), rounding, RectPart::AllCorners, ms) : _current; | ||||
| 			if (!_gif && (!_doc || !_doc->getStickerImage()) && toDraw.hasAlpha()) { | ||||
| 			const auto rounding = (_doc && _doc->isVideoMessage()) ? ImageRoundRadius::Ellipse : ImageRoundRadius::None; | ||||
| 			const auto toDraw = (_current.isNull() && _gif) ? _gif->current(_gif->width() / cIntRetinaFactor(), _gif->height() / cIntRetinaFactor(), _gif->width() / cIntRetinaFactor(), _gif->height() / cIntRetinaFactor(), rounding, RectPart::AllCorners, ms) : _current; | ||||
| 			if (!_gif && (!_doc || !_doc->getStickerLarge()) && (toDraw.hasAlpha() || toDraw.isNull())) { | ||||
| 				p.fillRect(imgRect, _transparentBrush); | ||||
| 			} | ||||
| 			if (toDraw.width() != _w * cIntRetinaFactor()) { | ||||
| 				PainterHighQualityEnabler hq(p); | ||||
| 				p.drawPixmap(QRect(_x, _y, _w, _h), toDraw); | ||||
| 			} else { | ||||
| 				p.drawPixmap(_x, _y, toDraw); | ||||
| 			if (!toDraw.isNull()) { | ||||
| 				if (toDraw.width() != _w * cIntRetinaFactor()) { | ||||
| 					PainterHighQualityEnabler hq(p); | ||||
| 					p.drawPixmap(QRect(_x, _y, _w, _h), toDraw); | ||||
| 				} else { | ||||
| 					p.drawPixmap(_x, _y, toDraw); | ||||
| 				} | ||||
| 			} | ||||
| 
 | ||||
| 			bool radial = false; | ||||
|  | @ -2165,7 +2186,7 @@ void MediaView::paintEvent(QPaintEvent *e) { | |||
| 						p.restoreTextPalette(); | ||||
| 						p.setOpacity(1); | ||||
| 					} | ||||
| 					if (_full >= 1) { | ||||
| 					if (!_blurred) { | ||||
|                         auto nextFrame = (dt < st::mediaviewSaveMsgShowing || hidingDt >= 0) ? int(AnimationTimerDelta) : (st::mediaviewSaveMsgShowing + st::mediaviewSaveMsgShown + 1 - dt); | ||||
| 						_saveMsgUpdater.start(nextFrame); | ||||
| 					} | ||||
|  | @ -2187,7 +2208,7 @@ void MediaView::paintEvent(QPaintEvent *e) { | |||
| 					radial = _radial.animating(); | ||||
| 					radialOpacity = _radial.opacity(); | ||||
| 				} | ||||
| 				if (!_doc || _doc->thumb->isNull()) { | ||||
| 				if (!_doc || !_doc->hasThumbnail()) { | ||||
| 					p.fillRect(_docIconRect, _docIconColor); | ||||
| 					if ((!_doc || _doc->loaded()) && (!radial || radialOpacity < 1) && _docIcon) { | ||||
| 						_docIcon->paint(p, _docIconRect.x() + (_docIconRect.width() - _docIcon->width()), _docIconRect.y(), width()); | ||||
|  | @ -2199,7 +2220,7 @@ void MediaView::paintEvent(QPaintEvent *e) { | |||
| 					} | ||||
| 				} else { | ||||
| 					int32 rf(cIntRetinaFactor()); | ||||
| 					p.drawPixmap(_docIconRect.topLeft(), _doc->thumb->pix(fileOrigin(), _docThumbw), QRect(_docThumbx * rf, _docThumby * rf, st::mediaviewFileIconSize * rf, st::mediaviewFileIconSize * rf)); | ||||
| 					p.drawPixmap(_docIconRect.topLeft(), _doc->thumbnail()->pix(fileOrigin(), _docThumbw), QRect(_docThumbx * rf, _docThumby * rf, st::mediaviewFileIconSize * rf, st::mediaviewFileIconSize * rf)); | ||||
| 				} | ||||
| 
 | ||||
| 				paintDocRadialLoading(p, radial, radialOpacity); | ||||
|  | @ -2720,10 +2741,10 @@ void MediaView::preloadData(int delta) { | |||
| 		if (auto photo = base::get_if<not_null<PhotoData*>>(&entity.data)) { | ||||
| 			(*photo)->download(fileOrigin()); | ||||
| 		} else if (auto document = base::get_if<not_null<DocumentData*>>(&entity.data)) { | ||||
| 			if (const auto image = (*document)->getStickerImage()) { | ||||
| 			if (const auto image = (*document)->getStickerLarge()) { | ||||
| 				image->load(fileOrigin()); | ||||
| 			} else { | ||||
| 				(*document)->thumb->load(fileOrigin()); | ||||
| 				(*document)->loadThumbnail(fileOrigin()); | ||||
| 				(*document)->automaticLoad(fileOrigin(), entity.item); | ||||
| 			} | ||||
| 		} | ||||
|  |  | |||
|  | @ -251,6 +251,9 @@ private: | |||
| 	void checkGroupThumbsAnimation(); | ||||
| 	void initGroupThumbs(); | ||||
| 
 | ||||
| 	void validatePhotoImage(Image *image, bool blurred); | ||||
| 	void validatePhotoCurrentImage(); | ||||
| 
 | ||||
| 	QBrush _transparentBrush; | ||||
| 
 | ||||
| 	PhotoData *_photo = nullptr; | ||||
|  | @ -299,7 +302,7 @@ private: | |||
| 	int32 _dragging = 0; | ||||
| 	QPixmap _current; | ||||
| 	Media::Clip::ReaderPointer _gif; | ||||
| 	int32 _full = -1; // -1 - thumb, 0 - medium, 1 - full
 | ||||
| 	bool _blurred = true; | ||||
| 
 | ||||
| 	// Video without audio stream playback information.
 | ||||
| 	bool _videoIsSilent = false; | ||||
|  |  | |||
|  | @ -328,33 +328,23 @@ 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->medium->automaticLoad(parent()->fullId(), parent()); | ||||
| 		good = _data->medium->loaded(); | ||||
| 		_data->thumbnail()->automaticLoad(parent()->fullId(), parent()); | ||||
| 		good = _data->thumbnail()->loaded(); | ||||
| 	} | ||||
| 	if ((good && !_goodLoaded) || _pix.width() != _width * cIntRetinaFactor()) { | ||||
| 		_goodLoaded = good; | ||||
| 
 | ||||
| 		int32 size = _width * cIntRetinaFactor(); | ||||
| 		if (_goodLoaded || _data->thumb->loaded()) { | ||||
| 			auto img = (_data->loaded() ? _data->full : (_data->medium->loaded() ? _data->medium : _data->thumb))->pix(parent()->fullId()).toImage(); | ||||
| 			if (!_goodLoaded) { | ||||
| 				img = Images::prepareBlur(std::move(img)); | ||||
| 		_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()) { | ||||
| 				setPixFrom(blurred); | ||||
| 			} | ||||
| 			if (img.width() == img.height()) { | ||||
| 				if (img.width() != size) { | ||||
| 					img = img.scaled(size, size, Qt::KeepAspectRatioByExpanding, Qt::SmoothTransformation); | ||||
| 				} | ||||
| 			} else if (img.width() > img.height()) { | ||||
| 				img = img.copy((img.width() - img.height()) / 2, 0, img.height(), img.height()).scaled(size, size, Qt::KeepAspectRatioByExpanding, Qt::SmoothTransformation); | ||||
| 			} else { | ||||
| 				img = img.copy(0, (img.height() - img.width()) / 2, img.width(), img.width()).scaled(size, size, Qt::KeepAspectRatioByExpanding, Qt::SmoothTransformation); | ||||
| 			} | ||||
| 			img.setDevicePixelRatio(cRetinaFactor()); | ||||
| 			_data->unload(); | ||||
| 
 | ||||
| 			_pix = App::pixmapFromImageInPlace(std::move(img)); | ||||
| 		} else if (!_pix.isNull()) { | ||||
| 			_pix = QPixmap(); | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
|  | @ -373,6 +363,29 @@ void Photo::paint(Painter &p, const QRect &clip, TextSelection selection, const | |||
| 	paintCheckbox(p, { checkLeft, checkTop }, selected, context); | ||||
| } | ||||
| 
 | ||||
| void Photo::setPixFrom(not_null<Image*> image) { | ||||
| 	Expects(image->loaded()); | ||||
| 
 | ||||
| 	const auto size = _width * cIntRetinaFactor(); | ||||
| 	auto img = image->original(); | ||||
| 	if (!_goodLoaded) { | ||||
| 		img = Images::prepareBlur(std::move(img)); | ||||
| 	} | ||||
| 	if (img.width() == img.height()) { | ||||
| 		if (img.width() != size) { | ||||
| 			img = img.scaled(size, size, Qt::KeepAspectRatioByExpanding, Qt::SmoothTransformation); | ||||
| 		} | ||||
| 	} else if (img.width() > img.height()) { | ||||
| 		img = img.copy((img.width() - img.height()) / 2, 0, img.height(), img.height()).scaled(size, size, Qt::KeepAspectRatioByExpanding, Qt::SmoothTransformation); | ||||
| 	} else { | ||||
| 		img = img.copy(0, (img.height() - img.width()) / 2, img.width(), img.width()).scaled(size, size, Qt::KeepAspectRatioByExpanding, Qt::SmoothTransformation); | ||||
| 	} | ||||
| 	img.setDevicePixelRatio(cRetinaFactor()); | ||||
| 	_data->unload(); | ||||
| 
 | ||||
| 	_pix = App::pixmapFromImageInPlace(std::move(img)); | ||||
| } | ||||
| 
 | ||||
| TextState Photo::getState( | ||||
| 		QPoint point, | ||||
| 		StateRequest request) const { | ||||
|  | @ -387,10 +400,9 @@ Video::Video( | |||
| 	not_null<DocumentData*> video) | ||||
| : RadialProgressItem(parent) | ||||
| , _data(video) | ||||
| , _duration(formatDurationText(_data->duration())) | ||||
| , _thumbLoaded(false) { | ||||
| , _duration(formatDurationText(_data->duration())) { | ||||
| 	setDocumentLinks(_data); | ||||
| 	_data->thumb->load(parent->fullId()); | ||||
| 	_data->loadThumbnail(parent->fullId()); | ||||
| } | ||||
| 
 | ||||
| void Video::initDimensions() { | ||||
|  | @ -405,7 +417,10 @@ int32 Video::resizeGetHeight(int32 width) { | |||
| } | ||||
| 
 | ||||
| void Video::paint(Painter &p, const QRect &clip, TextSelection selection, const PaintContext *context) { | ||||
| 	bool selected = (selection == FullSelection), thumbLoaded = _data->thumb->loaded(); | ||||
| 	const auto selected = (selection == FullSelection); | ||||
| 	const auto blurred = _data->thumbnailInline(); | ||||
| 	const auto thumbLoaded = _data->hasThumbnail() | ||||
| 		&& _data->thumbnail()->loaded(); | ||||
| 
 | ||||
| 	_data->automaticLoad(parent()->fullId(), parent()); | ||||
| 	bool loaded = _data->loaded(), displayLoading = _data->displayLoading(); | ||||
|  | @ -418,28 +433,25 @@ void Video::paint(Painter &p, const QRect &clip, TextSelection selection, const | |||
| 	updateStatusText(); | ||||
| 	bool radial = isRadialAnimation(context->ms); | ||||
| 
 | ||||
| 	if ((thumbLoaded && !_thumbLoaded) || (_pix.width() != _width * cIntRetinaFactor())) { | ||||
| 		_thumbLoaded = thumbLoaded; | ||||
| 
 | ||||
| 		if (_thumbLoaded && !_data->thumb->isNull()) { | ||||
| 			auto size = _width * cIntRetinaFactor(); | ||||
| 			auto img = Images::prepareBlur(_data->thumb->pix(parent()->fullId()).toImage()); | ||||
| 			if (img.width() == img.height()) { | ||||
| 				if (img.width() != size) { | ||||
| 					img = img.scaled(size, size, Qt::KeepAspectRatioByExpanding, Qt::SmoothTransformation); | ||||
| 				} | ||||
| 			} else if (img.width() > img.height()) { | ||||
| 				img = img.copy((img.width() - img.height()) / 2, 0, img.height(), img.height()).scaled(size, size, Qt::KeepAspectRatioByExpanding, Qt::SmoothTransformation); | ||||
| 			} else { | ||||
| 				img = img.copy(0, (img.height() - img.width()) / 2, img.width(), img.width()).scaled(size, size, Qt::KeepAspectRatioByExpanding, Qt::SmoothTransformation); | ||||
| 	if ((blurred || thumbLoaded) | ||||
| 		&& (_pix.width() != _width * cIntRetinaFactor())) { | ||||
| 		auto size = _width * cIntRetinaFactor(); | ||||
| 		auto img = thumbLoaded | ||||
| 			? _data->thumbnail()->original() | ||||
| 			: Images::prepareBlur(blurred->original()); | ||||
| 		if (img.width() == img.height()) { | ||||
| 			if (img.width() != size) { | ||||
| 				img = img.scaled(size, size, Qt::KeepAspectRatioByExpanding, Qt::SmoothTransformation); | ||||
| 			} | ||||
| 			img.setDevicePixelRatio(cRetinaFactor()); | ||||
| 			_data->unload(); | ||||
| 
 | ||||
| 			_pix = App::pixmapFromImageInPlace(std::move(img)); | ||||
| 		} else if (!_pix.isNull()) { | ||||
| 			_pix = QPixmap(); | ||||
| 		} else if (img.width() > img.height()) { | ||||
| 			img = img.copy((img.width() - img.height()) / 2, 0, img.height(), img.height()).scaled(size, size, Qt::KeepAspectRatioByExpanding, Qt::SmoothTransformation); | ||||
| 		} else { | ||||
| 			img = img.copy(0, (img.height() - img.width()) / 2, img.width(), img.width()).scaled(size, size, Qt::KeepAspectRatioByExpanding, Qt::SmoothTransformation); | ||||
| 		} | ||||
| 		img.setDevicePixelRatio(cRetinaFactor()); | ||||
| 		_data->unload(); | ||||
| 
 | ||||
| 		_pix = App::pixmapFromImageInPlace(std::move(img)); | ||||
| 	} | ||||
| 
 | ||||
| 	if (_pix.isNull()) { | ||||
|  | @ -582,7 +594,7 @@ Voice::Voice( | |||
| 	AddComponents(Info::Bit()); | ||||
| 
 | ||||
| 	setDocumentLinks(_data); | ||||
| 	_data->thumb->load(parent->fullId()); | ||||
| 	_data->loadThumbnail(parent->fullId()); | ||||
| 
 | ||||
| 	updateName(); | ||||
| 	const auto dateText = textcmdLink( | ||||
|  | @ -641,22 +653,28 @@ void Voice::paint(Painter &p, const QRect &clip, TextSelection selection, const | |||
| 		_width); | ||||
| 	if (clip.intersects(inner)) { | ||||
| 		p.setPen(Qt::NoPen); | ||||
| 		const auto drawThumb = !_data->thumb->isNull() | ||||
| 			&& _data->thumb->loaded(); | ||||
| 		if (drawThumb) { | ||||
| 			const auto thumb = _data->thumb->pixCircled( | ||||
| 				parent()->fullId(), | ||||
| 				inner.width(), | ||||
| 				inner.height()); | ||||
| 		const auto thumbLoaded = _data->hasThumbnail() | ||||
| 			&& _data->thumbnail()->loaded(); | ||||
| 		const auto blurred = _data->thumbnailInline(); | ||||
| 		if (thumbLoaded || blurred) { | ||||
| 			const auto thumb = thumbLoaded | ||||
| 				? _data->thumbnail()->pixCircled( | ||||
| 					parent()->fullId(), | ||||
| 					inner.width(), | ||||
| 					inner.height()) | ||||
| 				: blurred->pixBlurredCircled( | ||||
| 					parent()->fullId(), | ||||
| 					inner.width(), | ||||
| 					inner.height()); | ||||
| 			p.drawPixmap(inner.topLeft(), thumb); | ||||
| 		} else if (!_data->thumb->isNull()) { | ||||
| 		} else if (_data->hasThumbnail()) { | ||||
| 			PainterHighQualityEnabler hq(p); | ||||
| 			p.setBrush(st::imageBg); | ||||
| 			p.drawEllipse(inner); | ||||
| 		} | ||||
| 		if (selected) { | ||||
| 			p.setBrush(drawThumb ? st::msgDateImgBgSelected : st::msgFileInBgSelected); | ||||
| 		} else if (!_data->thumb->isNull()) { | ||||
| 			p.setBrush((thumbLoaded || blurred) ? st::msgDateImgBgSelected : st::msgFileInBgSelected); | ||||
| 		} else if (_data->hasThumbnail()) { | ||||
| 			auto over = ClickHandler::showAsActive(loaded ? _openl : (_data->loading() ? _cancell : _openl)); | ||||
| 			p.setBrush(anim::brush(st::msgDateImgBg, st::msgDateImgBgOver, _a_iconOver.current(context->ms, over ? 1. : 0.))); | ||||
| 		} else { | ||||
|  | @ -868,8 +886,8 @@ Document::Document( | |||
| 	_status.update(FileStatusSizeReady, _data->size, _data->isSong() ? _data->song()->duration : -1, 0); | ||||
| 
 | ||||
| 	if (withThumb()) { | ||||
| 		_data->thumb->load(parent->fullId()); | ||||
| 		int32 tw = ConvertScale(_data->thumb->width()), th = ConvertScale(_data->thumb->height()); | ||||
| 		_data->loadThumbnail(parent->fullId()); | ||||
| 		int32 tw = ConvertScale(_data->thumbnail()->width()), th = ConvertScale(_data->thumbnail()->height()); | ||||
| 		if (tw > th) { | ||||
| 			_thumbw = (tw * _st.fileThumbSize) / th; | ||||
| 		} else { | ||||
|  | @ -967,12 +985,16 @@ void Document::paint(Painter &p, const QRect &clip, TextSelection selection, con | |||
| 		QRect rthumb(rtlrect(0, st::linksBorder + _st.filePadding.top(), _st.fileThumbSize, _st.fileThumbSize, _width)); | ||||
| 		if (clip.intersects(rthumb)) { | ||||
| 			if (wthumb) { | ||||
| 				if (_data->thumb->loaded()) { | ||||
| 					if (_thumb.isNull() || loaded != _thumbForLoaded) { | ||||
| 						_thumbForLoaded = loaded; | ||||
| 				const auto thumbLoaded = _data->thumbnail()->loaded(); | ||||
| 				const auto blurred = _data->thumbnailInline(); | ||||
| 				if (thumbLoaded || blurred) { | ||||
| 					if (_thumb.isNull() || (thumbLoaded && !_thumbLoaded)) { | ||||
| 						_thumbLoaded = thumbLoaded; | ||||
| 						auto options = Images::Option::Smooth | Images::Option::None; | ||||
| 						if (!_thumbForLoaded) options |= Images::Option::Blurred; | ||||
| 						_thumb = _data->thumb->pixNoCache(parent()->fullId(), _thumbw * cIntRetinaFactor(), 0, options, _st.fileThumbSize, _st.fileThumbSize); | ||||
| 						if (!_thumbLoaded) options |= Images::Option::Blurred; | ||||
| 						_thumb = (_thumbLoaded | ||||
| 							? _data->thumbnail() | ||||
| 							: blurred)->pixNoCache(parent()->fullId(), _thumbw * cIntRetinaFactor(), 0, options, _st.fileThumbSize, _st.fileThumbSize); | ||||
| 					} | ||||
| 					p.drawPixmap(rthumb.topLeft(), _thumb); | ||||
| 				} else { | ||||
|  | @ -1132,7 +1154,7 @@ TextState Document::getState( | |||
| 				return { parent(), _msgl }; | ||||
| 			} | ||||
| 		} | ||||
| 		if (!_data->loading() && _data->isValid()) { | ||||
| 		if (!_data->loading() && !_data->isNull()) { | ||||
| 			auto leftofnamerect = rtlrect( | ||||
| 				0, | ||||
| 				st::linksBorder, | ||||
|  | @ -1180,9 +1202,9 @@ bool Document::iconAnimated() const { | |||
| 
 | ||||
| bool Document::withThumb() const { | ||||
| 	return !_data->isSong() | ||||
| 		&& !_data->thumb->isNull() | ||||
| 		&& _data->thumb->width() | ||||
| 		&& _data->thumb->height() | ||||
| 		&& _data->hasThumbnail() | ||||
| 		&& _data->thumbnail()->width() | ||||
| 		&& _data->thumbnail()->height() | ||||
| 		&& !Data::IsExecutableName(_data->filename()); | ||||
| } | ||||
| 
 | ||||
|  | @ -1304,19 +1326,19 @@ Link::Link( | |||
| 	} | ||||
| 	int32 tw = 0, th = 0; | ||||
| 	if (_page && _page->photo) { | ||||
| 		if (!_page->photo->loaded()) { | ||||
| 			_page->photo->thumb->load(parent->fullId(), false, false); | ||||
| 		if (!_page->photo->loaded() | ||||
| 			&& !_page->photo->thumbnail()->loaded() | ||||
| 			&& !_page->photo->thumbnailSmall()->loaded()) { | ||||
| 			_page->photo->loadThumbnailSmall(parent->fullId()); | ||||
| 		} | ||||
| 
 | ||||
| 		tw = ConvertScale(_page->photo->thumb->width()); | ||||
| 		th = ConvertScale(_page->photo->thumb->height()); | ||||
| 	} else if (_page && _page->document) { | ||||
| 		if (!_page->document->thumb->loaded()) { | ||||
| 			_page->document->thumb->load(parent->fullId(), false, false); | ||||
| 		} | ||||
| 		tw = ConvertScale(_page->photo->width()); | ||||
| 		th = ConvertScale(_page->photo->height()); | ||||
| 	} else if (_page && _page->document && _page->document->hasThumbnail()) { | ||||
| 		_page->document->loadThumbnail(parent->fullId()); | ||||
| 
 | ||||
| 		tw = ConvertScale(_page->document->thumb->width()); | ||||
| 		th = ConvertScale(_page->document->thumb->height()); | ||||
| 		tw = ConvertScale(_page->document->thumbnail()->width()); | ||||
| 		th = ConvertScale(_page->document->thumbnail()->height()); | ||||
| 	} | ||||
| 	if (tw > st::linksPhotoSize) { | ||||
| 		if (th > tw) { | ||||
|  | @ -1397,19 +1419,21 @@ void Link::paint(Painter &p, const QRect &clip, TextSelection selection, const P | |||
| 	if (clip.intersects(rtlrect(0, pixTop, st::linksPhotoSize, st::linksPhotoSize, _width))) { | ||||
| 		if (_page && _page->photo) { | ||||
| 			QPixmap pix; | ||||
| 			if (_page->photo->medium->loaded()) { | ||||
| 				pix = _page->photo->medium->pixSingle(parent()->fullId(), _pixw, _pixh, st::linksPhotoSize, st::linksPhotoSize, ImageRoundRadius::Small); | ||||
| 			if (_page->photo->thumbnail()->loaded()) { | ||||
| 				pix = _page->photo->thumbnail()->pixSingle(parent()->fullId(), _pixw, _pixh, st::linksPhotoSize, st::linksPhotoSize, ImageRoundRadius::Small); | ||||
| 			} else if (_page->photo->loaded()) { | ||||
| 				pix = _page->photo->full->pixSingle(parent()->fullId(), _pixw, _pixh, st::linksPhotoSize, st::linksPhotoSize, ImageRoundRadius::Small); | ||||
| 			} else { | ||||
| 				pix = _page->photo->thumb->pixSingle(parent()->fullId(), _pixw, _pixh, st::linksPhotoSize, st::linksPhotoSize, ImageRoundRadius::Small); | ||||
| 				pix = _page->photo->large()->pixSingle(parent()->fullId(), _pixw, _pixh, st::linksPhotoSize, st::linksPhotoSize, ImageRoundRadius::Small); | ||||
| 			} else if (_page->photo->thumbnailSmall()->loaded()) { | ||||
| 				pix = _page->photo->thumbnailSmall()->pixSingle(parent()->fullId(), _pixw, _pixh, st::linksPhotoSize, st::linksPhotoSize, ImageRoundRadius::Small); | ||||
| 			} else if (const auto blurred = _page->photo->thumbnailInline()) { | ||||
| 				pix = blurred->pixBlurredSingle(parent()->fullId(), _pixw, _pixh, st::linksPhotoSize, st::linksPhotoSize, ImageRoundRadius::Small); | ||||
| 			} | ||||
| 			p.drawPixmapLeft(pixLeft, pixTop, _width, pix); | ||||
| 		} else if (_page && _page->document && !_page->document->thumb->isNull()) { | ||||
| 		} else if (_page && _page->document && _page->document->hasThumbnail()) { | ||||
| 			auto roundRadius = _page->document->isVideoMessage() | ||||
| 				? ImageRoundRadius::Ellipse | ||||
| 				: ImageRoundRadius::Small; | ||||
| 			p.drawPixmapLeft(pixLeft, pixTop, _width, _page->document->thumb->pixSingle(parent()->fullId(), _pixw, _pixh, st::linksPhotoSize, st::linksPhotoSize, roundRadius)); | ||||
| 			p.drawPixmapLeft(pixLeft, pixTop, _width, _page->document->thumbnail()->pixSingle(parent()->fullId(), _pixw, _pixh, st::linksPhotoSize, st::linksPhotoSize, roundRadius)); | ||||
| 		} else { | ||||
| 			const auto index = _letter.isEmpty() | ||||
| 				? 0 | ||||
|  |  | |||
|  | @ -208,6 +208,8 @@ public: | |||
| 		StateRequest request) const override; | ||||
| 
 | ||||
| private: | ||||
| 	void setPixFrom(not_null<Image*> image); | ||||
| 
 | ||||
| 	not_null<PhotoData*> _data; | ||||
| 	ClickHandlerPtr _link; | ||||
| 
 | ||||
|  | @ -241,7 +243,6 @@ private: | |||
| 
 | ||||
| 	QString _duration; | ||||
| 	QPixmap _pix; | ||||
| 	bool _thumbLoaded = false; | ||||
| 
 | ||||
| 	void updateStatusText(); | ||||
| 
 | ||||
|  | @ -315,7 +316,7 @@ private: | |||
| 
 | ||||
| 	const style::OverviewFileLayout &_st; | ||||
| 
 | ||||
| 	bool _thumbForLoaded = false; | ||||
| 	bool _thumbLoaded = false; | ||||
| 	QPixmap _thumb; | ||||
| 
 | ||||
| 	Text _name; | ||||
|  |  | |||
|  | @ -146,7 +146,7 @@ void BackgroundRow::paintEvent(QPaintEvent *e) { | |||
| 	} | ||||
| 	if (radial) { | ||||
| 		const auto backThumb = App::main()->newBackgroundThumb(); | ||||
| 		if (backThumb->isNull()) { | ||||
| 		if (!backThumb) { | ||||
| 			p.drawPixmap(0, 0, _background); | ||||
| 		} else { | ||||
| 			const auto &pix = backThumb->pixBlurred( | ||||
|  |  | |||
|  | @ -150,11 +150,11 @@ void Uploader::uploadMedia( | |||
| 		Auth().data().processPhoto(media.photo, media.photoThumbs); | ||||
| 	} else if (media.type == SendMediaType::File | ||||
| 		|| media.type == SendMediaType::Audio) { | ||||
| 		const auto document = media.photoThumbs.isEmpty() | ||||
| 		const auto document = media.photoThumbs.empty() | ||||
| 			? Auth().data().processDocument(media.document) | ||||
| 			: Auth().data().processDocument( | ||||
| 				media.document, | ||||
| 				base::duplicate(media.photoThumbs.begin().value())); | ||||
| 				base::duplicate(media.photoThumbs.front().second)); | ||||
| 		if (!media.data.isEmpty()) { | ||||
| 			document->setData(media.data); | ||||
| 			if (document->saveToCache() | ||||
|  |  | |||
|  | @ -198,7 +198,7 @@ SendMediaReady PreparePeerPhoto(PeerId peerId, QImage &&image) { | |||
| 				MTP_long(0)), | ||||
| 			MTP_int(image.width()), | ||||
| 			MTP_int(image.height()), MTP_int(0))); | ||||
| 		photoThumbs.insert(type[0], std::move(image)); | ||||
| 		photoThumbs.emplace(type[0], std::move(image)); | ||||
| 	}; | ||||
| 	push("a", scaled(160)); | ||||
| 	push("b", scaled(320)); | ||||
|  | @ -795,15 +795,15 @@ void FileLoadTask::process() { | |||
| 				attributes.push_back(MTP_documentAttributeAnimated()); | ||||
| 			} else if (_type != SendMediaType::File) { | ||||
| 				auto thumb = (w > 100 || h > 100) ? fullimage.scaled(100, 100, Qt::KeepAspectRatio, Qt::SmoothTransformation) : fullimage; | ||||
| 				photoThumbs.insert('s', thumb); | ||||
| 				photoThumbs.emplace('s', thumb); | ||||
| 				photoSizes.push_back(MTP_photoSize(MTP_string("s"), MTP_fileLocationUnavailable(MTP_long(0), MTP_int(0), MTP_long(0)), MTP_int(thumb.width()), MTP_int(thumb.height()), MTP_int(0))); | ||||
| 
 | ||||
| 				auto medium = (w > 320 || h > 320) ? fullimage.scaled(320, 320, Qt::KeepAspectRatio, Qt::SmoothTransformation) : fullimage; | ||||
| 				photoThumbs.insert('m', medium); | ||||
| 				photoThumbs.emplace('m', medium); | ||||
| 				photoSizes.push_back(MTP_photoSize(MTP_string("m"), MTP_fileLocationUnavailable(MTP_long(0), MTP_int(0), MTP_long(0)), MTP_int(medium.width()), MTP_int(medium.height()), MTP_int(0))); | ||||
| 
 | ||||
| 				auto full = (w > 1280 || h > 1280) ? fullimage.scaled(1280, 1280, Qt::KeepAspectRatio, Qt::SmoothTransformation) : fullimage; | ||||
| 				photoThumbs.insert('y', full); | ||||
| 				photoThumbs.emplace('y', full); | ||||
| 				photoSizes.push_back(MTP_photoSize(MTP_string("y"), MTP_fileLocationUnavailable(MTP_long(0), MTP_int(0), MTP_long(0)), MTP_int(full.width()), MTP_int(full.height()), MTP_int(0))); | ||||
| 
 | ||||
| 				{ | ||||
|  |  | |||
|  | @ -3753,6 +3753,7 @@ void importOldRecentStickers() { | |||
| 			attributes, | ||||
| 			mime, | ||||
| 			ImagePtr(), | ||||
| 			ImagePtr(), | ||||
| 			dc, | ||||
| 			size, | ||||
| 			StorageImageLocation()); | ||||
|  |  | |||
|  | @ -49,7 +49,11 @@ void Document::writeToStream(QDataStream &stream, DocumentData *document) { | |||
| 		writeStorageImageLocation(stream, document->sticker()->loc); | ||||
| 	} else { | ||||
| 		stream << qint32(document->duration()); | ||||
| 		writeStorageImageLocation(stream, document->thumb->location()); | ||||
| 		if (const auto thumb = document->thumbnail()) { | ||||
| 			writeStorageImageLocation(stream, thumb->location()); | ||||
| 		} else { | ||||
| 			writeStorageImageLocation(stream, StorageImageLocation()); | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
|  | @ -137,6 +141,7 @@ DocumentData *Document::readFromStreamHelper(int streamAppVersion, QDataStream & | |||
| 		date, | ||||
| 		attributes, | ||||
| 		mime, | ||||
| 		ImagePtr(), | ||||
| 		thumb.isNull() ? ImagePtr() : Images::Create(thumb), | ||||
| 		dc, | ||||
| 		size, | ||||
|  | @ -172,7 +177,11 @@ int Document::sizeInStream(DocumentData *document) { | |||
| 		// + duration
 | ||||
| 		result += sizeof(qint32); | ||||
| 		// + thumb loc
 | ||||
| 		result += Serialize::storageImageLocationSize(document->thumb->location()); | ||||
| 		if (const auto thumb = document->thumbnail()) { | ||||
| 			result += Serialize::storageImageLocationSize(thumb->location()); | ||||
| 		} else { | ||||
| 			result += Serialize::storageImageLocationSize(StorageImageLocation()); | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	return result; | ||||
|  |  | |||
|  | @ -755,7 +755,9 @@ QImage Image::original() const { | |||
| 	return _data; | ||||
| } | ||||
| 
 | ||||
| void Image::automaticLoad(Data::FileOrigin origin, const HistoryItem *item) { | ||||
| void Image::automaticLoad( | ||||
| 		Data::FileOrigin origin, | ||||
| 		const HistoryItem *item) { | ||||
| 	if (!loaded()) { | ||||
| 		_source->automaticLoad(origin, item); | ||||
| 	} | ||||
|  |  | |||
|  | @ -198,6 +198,9 @@ public: | |||
| 	int height() const { | ||||
| 		return _source->height(); | ||||
| 	} | ||||
| 	QSize size() const { | ||||
| 		return { width(), height() }; | ||||
| 	} | ||||
| 	int bytesSize() const { | ||||
| 		return _source->bytesSize(); | ||||
| 	} | ||||
|  |  | |||
|  | @ -49,6 +49,9 @@ QPixmap PixmapFast(QImage &&image) { | |||
| } | ||||
| 
 | ||||
| QImage prepareBlur(QImage img) { | ||||
| 	if (img.isNull()) { | ||||
| 		return img; | ||||
| 	} | ||||
| 	auto ratio = img.devicePixelRatio(); | ||||
| 	auto fmt = img.format(); | ||||
| 	if (fmt != QImage::Format_RGB32 && fmt != QImage::Format_ARGB32_Premultiplied) { | ||||
|  |  | |||
|  | @ -892,11 +892,6 @@ void MediaPreviewWidget::showPreview( | |||
| void MediaPreviewWidget::showPreview( | ||||
| 		Data::FileOrigin origin, | ||||
| 		not_null<PhotoData*> photo) { | ||||
| 	if (photo->full->isNull()) { | ||||
| 		hidePreview(); | ||||
| 		return; | ||||
| 	} | ||||
| 
 | ||||
| 	startShow(); | ||||
| 	_origin = origin; | ||||
| 	_photo = photo; | ||||
|  | @ -965,7 +960,7 @@ QSize MediaPreviewWidget::currentDimensions() const { | |||
| 
 | ||||
| 	QSize result, box; | ||||
| 	if (_photo) { | ||||
| 		result = QSize(_photo->full->width(), _photo->full->height()); | ||||
| 		result = QSize(_photo->width(), _photo->height()); | ||||
| 		box = QSize(width() - 2 * st::boxVerticalMargin, height() - 2 * st::boxVerticalMargin); | ||||
| 	} else { | ||||
| 		result = _document->dimensions; | ||||
|  | @ -997,13 +992,15 @@ QPixmap MediaPreviewWidget::currentImage() const { | |||
| 	if (_document) { | ||||
| 		if (_document->sticker()) { | ||||
| 			if (_cacheStatus != CacheLoaded) { | ||||
| 				if (const auto image = _document->getStickerImage()) { | ||||
| 				if (const auto image = _document->getStickerLarge()) { | ||||
| 					QSize s = currentDimensions(); | ||||
| 					_cache = image->pix(_origin, s.width(), s.height()); | ||||
| 					_cacheStatus = CacheLoaded; | ||||
| 				} else if (_cacheStatus != CacheThumbLoaded && _document->thumb->loaded()) { | ||||
| 				} else if (_cacheStatus != CacheThumbLoaded | ||||
| 					&& _document->hasThumbnail() | ||||
| 					&& _document->thumbnail()->loaded()) { | ||||
| 					QSize s = currentDimensions(); | ||||
| 					_cache = _document->thumb->pixBlurred(_origin, s.width(), s.height()); | ||||
| 					_cache = _document->thumbnail()->pixBlurred(_origin, s.width(), s.height()); | ||||
| 					_cacheStatus = CacheThumbLoaded; | ||||
| 				} | ||||
| 			} | ||||
|  | @ -1023,26 +1020,43 @@ QPixmap MediaPreviewWidget::currentImage() const { | |||
| 				auto paused = _controller->isGifPausedAtLeastFor(Window::GifPauseReason::MediaPreview); | ||||
| 				return _gif->current(s.width(), s.height(), s.width(), s.height(), ImageRoundRadius::None, RectPart::None, paused ? 0 : getms()); | ||||
| 			} | ||||
| 			if (_cacheStatus != CacheThumbLoaded && _document->thumb->loaded()) { | ||||
| 			if (_cacheStatus != CacheThumbLoaded | ||||
| 				&& _document->hasThumbnail()) { | ||||
| 				QSize s = currentDimensions(); | ||||
| 				_cache = _document->thumb->pixBlurred(_origin, s.width(), s.height()); | ||||
| 				_cacheStatus = CacheThumbLoaded; | ||||
| 				if (_document->thumbnail()->loaded()) { | ||||
| 					_cache = _document->thumbnail()->pixBlurred(_origin, s.width(), s.height()); | ||||
| 					_cacheStatus = CacheThumbLoaded; | ||||
| 				} else if (const auto blurred = _document->thumbnailInline()) { | ||||
| 					_cache = _document->thumbnail()->pixBlurred(_origin, s.width(), s.height()); | ||||
| 					_cacheStatus = CacheThumbLoaded; | ||||
| 				} else { | ||||
| 					_document->thumbnail()->load(_origin); | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 	} else if (_photo) { | ||||
| 		if (_cacheStatus != CacheLoaded) { | ||||
| 			if (_photo->full->loaded()) { | ||||
| 			if (_photo->loaded()) { | ||||
| 				QSize s = currentDimensions(); | ||||
| 				_cache = _photo->full->pix(_origin, s.width(), s.height()); | ||||
| 				_cache = _photo->large()->pix(_origin, s.width(), s.height()); | ||||
| 				_cacheStatus = CacheLoaded; | ||||
| 			} else { | ||||
| 				if (_cacheStatus != CacheThumbLoaded && _photo->thumb->loaded()) { | ||||
| 				_photo->load(_origin); | ||||
| 				if (_cacheStatus != CacheThumbLoaded) { | ||||
| 					QSize s = currentDimensions(); | ||||
| 					_cache = _photo->thumb->pixBlurred(_origin, s.width(), s.height()); | ||||
| 					_cacheStatus = CacheThumbLoaded; | ||||
| 					if (_photo->thumbnail()->loaded()) { | ||||
| 						_cache = _photo->thumbnail()->pixBlurred(_origin, s.width(), s.height()); | ||||
| 						_cacheStatus = CacheThumbLoaded; | ||||
| 					} else if (_photo->thumbnailSmall()->loaded()) { | ||||
| 						_cache = _photo->thumbnailSmall()->pixBlurred(_origin, s.width(), s.height()); | ||||
| 						_cacheStatus = CacheThumbLoaded; | ||||
| 					} else if (const auto blurred = _photo->thumbnailInline()) { | ||||
| 						_cache = blurred->pixBlurred(_origin, s.width(), s.height()); | ||||
| 						_cacheStatus = CacheThumbLoaded; | ||||
| 					} else { | ||||
| 						_photo->thumbnailSmall()->load(_origin); | ||||
| 					} | ||||
| 				} | ||||
| 				_photo->thumb->load(_origin); | ||||
| 				_photo->full->load(_origin); | ||||
| 			} | ||||
| 		} | ||||
| 
 | ||||
|  |  | |||
|  | @ -7,6 +7,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL | |||
| */ | ||||
| #pragma once | ||||
| 
 | ||||
| class Image; | ||||
| 
 | ||||
| namespace Data { | ||||
| 
 | ||||
| struct WallPaper { | ||||
|  | @ -14,7 +16,7 @@ struct WallPaper { | |||
| 	uint64 accessHash = 0; | ||||
| 	MTPDwallPaper::Flags flags; | ||||
| 	QString slug; | ||||
| 	ImagePtr thumb; | ||||
| 	Image *thumb = nullptr; | ||||
| 	DocumentData *document = nullptr; | ||||
| }; | ||||
| 
 | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue