Use both thumbnails in photos and documents.

Fixes #5602.
This commit is contained in:
John Preston 2019-01-25 18:37:28 +04:00
parent a70e72f75d
commit a1baa23a52
56 changed files with 1358 additions and 878 deletions

View File

@ -894,7 +894,7 @@ void ApiWrap::requestWallPaper(
data.vaccess_hash.v, data.vaccess_hash.v,
data.vflags.v, data.vflags.v,
qs(data.vslug), qs(data.vslug),
document->thumb, document->thumbnail(),
document document
}); });
} }

View File

@ -95,7 +95,7 @@ QImage PrepareScaledFromFull(
size); size);
} }
QPixmap PrepareScaledFromThumb(ImagePtr thumb) { QPixmap PrepareScaledFromThumb(not_null<Image*> thumb) {
return thumb->loaded() return thumb->loaded()
? App::pixmapFromImageInPlace(PrepareScaledFromFull( ? App::pixmapFromImageInPlace(PrepareScaledFromFull(
thumb->original(), thumb->original(),
@ -315,7 +315,7 @@ void BackgroundPreviewBox::prepare() {
_scaled = PrepareScaledFromThumb(_paper.thumb); _scaled = PrepareScaledFromThumb(_paper.thumb);
checkLoadedDocument(); checkLoadedDocument();
if (_paper.thumb && !_paper.thumb->loaded()) { if (!_paper.thumb->loaded()) {
_paper.thumb->loadEvenCancelled(Data::FileOriginWallpaper( _paper.thumb->loadEvenCancelled(Data::FileOriginWallpaper(
_paper.id, _paper.id,
_paper.accessHash)); _paper.accessHash));

View File

@ -42,17 +42,17 @@ EditCaptionBox::EditCaptionBox(
Expects(item->media()->allowsEditCaption()); Expects(item->media()->allowsEditCaption());
QSize dimensions; QSize dimensions;
ImagePtr image; auto image = (Image*)nullptr;
DocumentData *doc = nullptr; DocumentData *doc = nullptr;
const auto media = item->media(); const auto media = item->media();
if (const auto photo = media->photo()) { if (const auto photo = media->photo()) {
_photo = true; _photo = true;
dimensions = QSize(photo->full->width(), photo->full->height()); dimensions = QSize(photo->width(), photo->height());
image = photo->full; image = photo->large();
} else if (const auto document = media->document()) { } else if (const auto document = media->document()) {
dimensions = document->dimensions; dimensions = document->dimensions;
image = document->thumb; image = document->thumbnail();
if (document->isAnimation()) { if (document->isAnimation()) {
_animated = true; _animated = true;
} else if (document->isVideoFile()) { } else if (document->isVideoFile()) {
@ -68,8 +68,8 @@ EditCaptionBox::EditCaptionBox(
ConvertEntitiesToTextTags(original.entities) ConvertEntitiesToTextTags(original.entities)
}; };
if (!_animated && (dimensions.isEmpty() || doc || image->isNull())) { if (!_animated && (dimensions.isEmpty() || doc || !image)) {
if (image->isNull()) { if (!image) {
_thumbw = 0; _thumbw = 0;
} else { } else {
int32 tw = image->width(), th = image->height(); int32 tw = image->width(), th = image->height();

View File

@ -63,7 +63,7 @@ private:
not_null<Window::Controller*> _controller; not_null<Window::Controller*> _controller;
FullMsgId _msgId; FullMsgId _msgId;
ImagePtr _thumbnailImage; Image *_thumbnailImage = nullptr;
bool _thumbnailImageLoaded = false; bool _thumbnailImageLoaded = false;
Fn<void()> _refreshThumbnail; Fn<void()> _refreshThumbnail;
bool _animated = false; bool _animated = false;

View File

@ -472,7 +472,7 @@ void StickerSetBox::Inner::paintEvent(QPaintEvent *e) {
p.setOpacity(1); 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())); 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; if (coef > 1) coef = 1;
@ -480,7 +480,7 @@ void StickerSetBox::Inner::paintEvent(QPaintEvent *e) {
if (w < 1) w = 1; if (w < 1) w = 1;
if (h < 1) h = 1; if (h < 1) h = 1;
QPoint ppos = pos + QPoint((st::stickersSize.width() - w) / 2, (st::stickersSize.height() - h) / 2); 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( p.drawPixmapLeft(
ppos, ppos,
width(), width(),

View File

@ -804,9 +804,11 @@ void StickersBox::Inner::paintRow(Painter &p, Row *set, int index, TimeMs ms) {
const auto origin = Data::FileOriginStickerSet( const auto origin = Data::FileOriginStickerSet(
set->id, set->id,
set->accessHash); set->accessHash);
set->sticker->thumb->load(origin); if (const auto thumb = set->sticker->thumbnail()) {
auto pix = set->sticker->thumb->pix(origin, set->pixw, set->pixh); thumb->load(origin);
p.drawPixmapLeft(stickerx + (st::contactsPhotoSize - set->pixw) / 2, st::contactsPadding.top() + (st::contactsPhotoSize - set->pixh) / 2, width(), pix); 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(); 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 sticker = *outSticker = set.stickers.front();
auto pixw = sticker->thumb->width(); const auto size = sticker->thumbnail()
auto pixh = sticker->thumb->height(); ? sticker->thumbnail()->size()
: QSize(1, 1);
auto pixw = size.width();
auto pixh = size.height();
if (pixw > st::contactsPhotoSize) { if (pixw > st::contactsPhotoSize) {
if (pixw > pixh) { if (pixw > pixh) {
pixh = (pixh * st::contactsPhotoSize) / pixw; pixh = (pixh * st::contactsPhotoSize) / pixw;
@ -1734,7 +1739,10 @@ void StickersBox::Inner::readVisibleSets() {
if (i * _rowHeight < itemsVisibleTop || (i + 1) * _rowHeight > itemsVisibleBottom) { if (i * _rowHeight < itemsVisibleTop || (i + 1) * _rowHeight > itemsVisibleBottom) {
continue; 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); Auth().api().readFeaturedSetDelayed(_rows[i]->id);
} }
} }

View File

@ -503,34 +503,39 @@ void Panel::processUserPhoto() {
_user->loadUserpic(true); _user->loadUserpic(true);
} }
const auto photo = _user->userpicPhotoId() const auto photo = _user->userpicPhotoId()
? Auth().data().photo(_user->userpicPhotoId()).get() ? _user->owner().photo(_user->userpicPhotoId()).get()
: nullptr; : nullptr;
if (isGoodUserPhoto(photo)) { if (isGoodUserPhoto(photo)) {
photo->full->load(_user->userpicPhotoOrigin(), true); photo->large()->load(_user->userpicPhotoOrigin(), true);
} else if (_user->userpicPhotoUnknown() || (photo && !photo->date)) { } else if (_user->userpicPhotoUnknown() || (photo && !photo->date)) {
Auth().api().requestFullPeer(_user); _user->session().api().requestFullPeer(_user);
} }
refreshUserPhoto(); refreshUserPhoto();
} }
void Panel::refreshUserPhoto() { void Panel::refreshUserPhoto() {
const auto photo = _user->userpicPhotoId() const auto photo = _user->userpicPhotoId()
? Auth().data().photo(_user->userpicPhotoId()).get() ? _user->owner().photo(_user->userpicPhotoId()).get()
: nullptr; : nullptr;
const auto isNewPhoto = [&](not_null<PhotoData*> photo) { const auto isNewPhoto = [&](not_null<PhotoData*> photo) {
return photo->full->loaded() return photo->large()->loaded()
&& (photo->id != _userPhotoId || !_userPhotoFull); && (photo->id != _userPhotoId || !_userPhotoFull);
}; };
if (isGoodUserPhoto(photo) && isNewPhoto(photo)) { if (isGoodUserPhoto(photo) && isNewPhoto(photo)) {
_userPhotoId = photo->id; _userPhotoId = photo->id;
_userPhotoFull = true; _userPhotoFull = true;
createUserpicCache(photo->full, _user->userpicPhotoOrigin()); createUserpicCache(
photo->isNull() ? nullptr : photo->large().get(),
_user->userpicPhotoOrigin());
} else if (_userPhoto.isNull()) { } 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 size = st::callWidth * cIntRetinaFactor();
auto options = _useTransparency ? (Images::Option::RoundedLarge | Images::Option::RoundedTopLeft | Images::Option::RoundedTopRight | Images::Option::Smooth) : Images::Option::None; auto options = _useTransparency ? (Images::Option::RoundedLarge | Images::Option::RoundedTopLeft | Images::Option::RoundedTopRight | Images::Option::Smooth) : Images::Option::None;
if (image) { if (image) {
@ -570,14 +575,14 @@ void Panel::createUserpicCache(ImagePtr image, Data::FileOrigin origin) {
} }
bool Panel::isGoodUserPhoto(PhotoData *photo) { bool Panel::isGoodUserPhoto(PhotoData *photo) {
if (!photo || !photo->date) { if (!photo || photo->isNull()) {
return false; return false;
} }
auto badAspect = [](int a, int b) { const auto badAspect = [](int a, int b) {
return a > 10 * b; return a > 10 * b;
}; };
auto width = photo->full->width(); const auto width = photo->width();
auto height = photo->full->height(); const auto height = photo->height();
return !badAspect(width, height) && !badAspect(height, width); return !badAspect(width, height) && !badAspect(height, width);
} }

View File

@ -92,7 +92,7 @@ private:
void processUserPhoto(); void processUserPhoto();
void refreshUserPhoto(); void refreshUserPhoto();
bool isGoodUserPhoto(PhotoData *photo); bool isGoodUserPhoto(PhotoData *photo);
void createUserpicCache(ImagePtr image, Data::FileOrigin origin); void createUserpicCache(Image *image, Data::FileOrigin origin);
QRect signalBarsRect() const; QRect signalBarsRect() const;
void paintSignalBarsBg(Painter &p); void paintSignalBarsBg(Painter &p);

View File

@ -561,7 +561,7 @@ void FieldAutocompleteInner::paintEvent(QPaintEvent *e) {
App::roundRect(p, QRect(tl, st::stickerPanSize), st::emojiPanHover, StickerHoverCorners); 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())); 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; if (coef > 1) coef = 1;
@ -569,7 +569,7 @@ void FieldAutocompleteInner::paintEvent(QPaintEvent *e) {
if (w < 1) w = 1; if (w < 1) w = 1;
if (h < 1) h = 1; if (h < 1) h = 1;
QPoint ppos = pos + QPoint((st::stickerPanSize.width() - w) / 2, (st::stickerPanSize.height() - h) / 2); 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)); p.drawPixmapLeft(ppos, width(), image->pix(document->stickerSetOrigin(), w, h));
} }
} }

View File

@ -352,11 +352,10 @@ void GifsListWidget::selectInlineResult(int row, int column) {
auto item = _rows[row].items[column]; auto item = _rows[row].items[column];
if (const auto photo = item->getPhoto()) { if (const auto photo = item->getPhoto()) {
if (photo->medium->loaded() || photo->thumb->loaded()) { if (photo->thumbnail()->loaded()) {
_photoChosen.fire_copy(photo); _photoChosen.fire_copy(photo);
} else if (!photo->medium->loading()) { } else if (!photo->thumbnail()->loading()) {
photo->thumb->loadEvenCancelled(Data::FileOrigin()); photo->thumbnail()->loadEvenCancelled(Data::FileOrigin());
photo->medium->loadEvenCancelled(Data::FileOrigin());
} }
} else if (const auto document = item->getDocument()) { } else if (const auto document = item->getDocument()) {
if (document->loaded()) { if (document->loaded()) {

View File

@ -259,8 +259,8 @@ void StickersListWidget::Footer::enumerateVisibleIcons(Callback callback) {
void StickersListWidget::Footer::preloadImages() { void StickersListWidget::Footer::preloadImages() {
enumerateVisibleIcons([](const StickerIcon &icon, int x) { enumerateVisibleIcons([](const StickerIcon &icon, int x) {
if (auto sticker = icon.sticker) { if (const auto sticker = icon.sticker) {
sticker->thumb->load(sticker->stickerSetOrigin()); sticker->loadThumbnail(sticker->stickerSetOrigin());
} }
}); });
} }
@ -631,10 +631,12 @@ void StickersListWidget::Footer::paintSetIcon(
int x) const { int x) const {
if (icon.sticker) { if (icon.sticker) {
const auto origin = icon.sticker->stickerSetOrigin(); const auto origin = icon.sticker->stickerSetOrigin();
icon.sticker->thumb->load(origin); if (const auto thumb = icon.sticker->thumbnail()) {
auto pix = icon.sticker->thumb->pix(origin, icon.pixw, icon.pixh); 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) { } else if (icon.megagroup) {
icon.megagroup->paintUserpicLeft(p, x + (st::stickerIconWidth - st::stickerGroupCategorySize) / 2, _iconsTop + (st::emojiFooterHeight - st::stickerGroupCategorySize) / 2, width(), st::stickerGroupCategorySize); icon.megagroup->paintUserpicLeft(p, x + (st::stickerIconWidth - st::stickerGroupCategorySize) / 2, _iconsTop + (st::emojiFooterHeight - st::stickerGroupCategorySize) / 2, width(), st::stickerGroupCategorySize);
} else { } else {
@ -753,7 +755,9 @@ void StickersListWidget::readVisibleSets() {
int count = qMin(set.pack.size(), _columnCount); int count = qMin(set.pack.size(), _columnCount);
int loaded = 0; int loaded = 0;
for (int j = 0; j < count; ++j) { 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; ++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); 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())); 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; if (coef > 1) coef = 1;
auto w = qMax(qRound(coef * document->dimensions.width()), 1); auto w = qMax(qRound(coef * document->dimensions.width()), 1);
auto h = qMax(qRound(coef * document->dimensions.height()), 1); auto h = qMax(qRound(coef * document->dimensions.height()), 1);
auto ppos = pos + QPoint((_singleSize.width() - w) / 2, (_singleSize.height() - h) / 2); 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()) { if (image->loaded()) {
p.drawPixmapLeft( p.drawPixmapLeft(
ppos, ppos,
@ -1786,7 +1790,7 @@ void StickersListWidget::preloadImages() {
const auto document = sets[i].pack[j]; const auto document = sets[i].pack[j];
if (!document || !document->sticker()) continue; if (!document || !document->sticker()) continue;
document->checkStickerThumb(); document->checkStickerSmall();
} }
if (k > _columnCount * (_columnCount + 1)) break; if (k > _columnCount * (_columnCount + 1)) break;
} }
@ -2047,7 +2051,10 @@ void StickersListWidget::fillIcons(QList<StickerIcon> &icons) {
} }
auto s = _mySets[i].pack[0]; auto s = _mySets[i].pack[0];
auto availw = st::stickerIconWidth - 2 * st::stickerIconPadding, availh = st::emojiFooterHeight - 2 * st::stickerIconPadding; 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) { if (availw * thumbh > availh * thumbw) {
pixh = availh; pixh = availh;
pixw = (pixh * thumbw) / thumbh; pixw = (pixh * thumbw) / thumbh;

View File

@ -25,7 +25,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "boxes/confirm_box.h" #include "boxes/confirm_box.h"
#include "ui/image/image.h" #include "ui/image/image.h"
#include "ui/image/image_source.h" #include "ui/image/image_source.h"
#include "auth_session.h"
#include "mainwindow.h" #include "mainwindow.h"
#include "core/application.h" #include "core/application.h"
@ -318,7 +317,7 @@ void DocumentOpenClickHandler::Open(
auto audio = AudioMsgId(data, msgId); auto audio = AudioMsgId(data, msgId);
Media::Player::mixer()->play(audio); Media::Player::mixer()->play(audio);
Media::Player::Updated().notify(audio); Media::Player::Updated().notify(audio);
data->session()->data().markMediaRead(data); data->owner().markMediaRead(data);
} }
} else if (playMusic) { } else if (playMusic) {
auto state = Media::Player::mixer()->currentState(AudioMsgId::Type::Song); auto state = Media::Player::mixer()->currentState(AudioMsgId::Type::Song);
@ -345,24 +344,24 @@ void DocumentOpenClickHandler::Open(
File::Launch(filepath); File::Launch(filepath);
} }
} }
data->session()->data().markMediaRead(data); data->owner().markMediaRead(data);
} else if (data->isVoiceMessage() || data->isAudioFile() || data->isVideoFile()) { } else if (data->isVoiceMessage() || data->isAudioFile() || data->isVideoFile()) {
const auto filepath = location.name(); const auto filepath = location.name();
if (Data::IsValidMediaFile(filepath)) { if (Data::IsValidMediaFile(filepath)) {
File::Launch(filepath); File::Launch(filepath);
} }
data->session()->data().markMediaRead(data); data->owner().markMediaRead(data);
} else if (data->size < App::kImageSizeLimit) { } else if (data->size < App::kImageSizeLimit) {
if (!data->data().isEmpty() && playAnimation) { if (!data->data().isEmpty() && playAnimation) {
if (action == ActionOnLoadPlayInline && context) { if (action == ActionOnLoadPlayInline && context) {
data->session()->data().requestAnimationPlayInline(context); data->owner().requestAnimationPlayInline(context);
} else { } else {
Core::App().showDocument(data, context); Core::App().showDocument(data, context);
} }
} else if (location.accessEnable()) { } else if (location.accessEnable()) {
if (playAnimation || QImageReader(location.name()).canRead()) { if (playAnimation || QImageReader(location.name()).canRead()) {
if (playAnimation && action == ActionOnLoadPlayInline && context) { if (playAnimation && action == ActionOnLoadPlayInline && context) {
data->session()->data().requestAnimationPlayInline(context); data->owner().requestAnimationPlayInline(context);
} else { } else {
Core::App().showDocument(data, context); 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) : id(id)
, _session(session) { , _owner(owner) {
} }
not_null<AuthSession*> DocumentData::session() const { Data::Session &DocumentData::owner() const {
return _session; return *_owner;
}
AuthSession &DocumentData::session() const {
return _owner->session();
} }
void DocumentData::setattributes(const QVector<MTPDocumentAttribute> &attributes) { void DocumentData::setattributes(const QVector<MTPDocumentAttribute> &attributes) {
@ -581,7 +584,7 @@ bool DocumentData::checkWallPaperProperties() {
return true; return true;
} }
if (type != FileDocument if (type != FileDocument
|| !thumb || !_thumbnail
|| !dimensions.width() || !dimensions.width()
|| !dimensions.height() || !dimensions.height()
|| dimensions.width() > Storage::kMaxWallPaperDimension || dimensions.width() > Storage::kMaxWallPaperDimension
@ -593,10 +596,43 @@ bool DocumentData::checkWallPaperProperties() {
return true; 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 { bool DocumentData::isWallPaper() const {
return (type == WallPaperDocument); 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 { Storage::Cache::Key DocumentData::goodThumbnailCacheKey() const {
return Data::DocumentThumbCacheKey(_dc, id); return Data::DocumentThumbCacheKey(_dc, id);
} }
@ -645,14 +681,19 @@ bool DocumentData::saveToCache() const {
void DocumentData::unload() { void DocumentData::unload() {
// Forget thumb only when image cache limit exceeds. // 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()) {
if (sticker()->image) { if (sticker()->image) {
ActiveCache().decrement(ComputeUsage(sticker())); ActiveCache().decrement(ComputeUsage(sticker()));
sticker()->image = nullptr; sticker()->image = nullptr;
} }
} }
_replyPreview = nullptr; _replyPreview.clear();
if (!_data.isEmpty()) { if (!_data.isEmpty()) {
ActiveCache().decrement(_data.size()); ActiveCache().decrement(_data.size());
_data.clear(); _data.clear();
@ -737,7 +778,7 @@ void DocumentData::performActionOnLoad() {
} }
} else if (Media::Player::IsStopped(state.state)) { } else if (Media::Player::IsStopped(state.state)) {
Media::Player::mixer()->play(AudioMsgId(this, _actionOnLoadMsgId)); Media::Player::mixer()->play(AudioMsgId(this, _actionOnLoadMsgId));
_session->data().markMediaRead(this); _owner->markMediaRead(this);
} }
} }
} else if (playMusic) { } else if (playMusic) {
@ -758,7 +799,7 @@ void DocumentData::performActionOnLoad() {
} else if (playAnimation) { } else if (playAnimation) {
if (loaded()) { if (loaded()) {
if (_actionOnLoad == ActionOnLoadPlayInline && item) { if (_actionOnLoad == ActionOnLoadPlayInline && item) {
_session->data().requestAnimationPlayInline(item); _owner->requestAnimationPlayInline(item);
} else { } else {
Core::App().showDocument(this, item); Core::App().showDocument(this, item);
} }
@ -773,7 +814,7 @@ void DocumentData::performActionOnLoad() {
if (Data::IsValidMediaFile(already)) { if (Data::IsValidMediaFile(already)) {
File::Launch(already); File::Launch(already);
} }
_session->data().markMediaRead(this); _owner->markMediaRead(this);
} else if (loc.accessEnable()) { } else if (loc.accessEnable()) {
if (showImage && QImageReader(loc.name()).canRead()) { if (showImage && QImageReader(loc.name()).canRead()) {
Core::App().showDocument(this, item); Core::App().showDocument(this, item);
@ -810,14 +851,14 @@ bool DocumentData::loaded(FilePathResolveType type) const {
_loader->imageData())); _loader->imageData()));
ActiveCache().increment(ComputeUsage(that->sticker())); ActiveCache().increment(ComputeUsage(that->sticker()));
} }
if (!that->_data.isEmpty() || that->getStickerImage()) { if (!that->_data.isEmpty() || that->getStickerLarge()) {
ActiveCache().up(that); ActiveCache().up(that);
} }
that->refreshGoodThumbnail(); that->refreshGoodThumbnail();
destroyLoader(); destroyLoader();
} }
_session->data().notifyDocumentLayoutChanged(this); _owner->notifyDocumentLayoutChanged(this);
} }
return !data().isEmpty() || !filepath(type).isEmpty(); return !data().isEmpty() || !filepath(type).isEmpty();
} }
@ -962,7 +1003,7 @@ void DocumentData::save(
if (loading()) { if (loading()) {
_loader->start(); _loader->start();
} }
_session->data().notifyDocumentLayoutChanged(this); _owner->notifyDocumentLayoutChanged(this);
} }
void DocumentData::cancel() { void DocumentData::cancel() {
@ -971,7 +1012,7 @@ void DocumentData::cancel() {
} }
destroyLoader(CancelledMtpFileLoader); destroyLoader(CancelledMtpFileLoader);
_session->data().notifyDocumentLayoutChanged(this); _owner->notifyDocumentLayoutChanged(this);
App::main()->documentLoadProgress(this); App::main()->documentLoadProgress(this);
_actionOnLoad = ActionOnLoadNone; _actionOnLoad = ActionOnLoadNone;
@ -1085,7 +1126,7 @@ bool DocumentData::isStickerSetInstalled() const {
Expects(sticker() != nullptr); Expects(sticker() != nullptr);
const auto &set = sticker()->set; const auto &set = sticker()->set;
const auto &sets = _session->data().stickerSets(); const auto &sets = _owner->stickerSets();
switch (set.type()) { switch (set.type()) {
case mtpc_inputStickerSetID: { case mtpc_inputStickerSetID: {
auto it = sets.constFind(set.c_inputStickerSetID().vid.v); auto it = sets.constFind(set.c_inputStickerSetID().vid.v);
@ -1107,25 +1148,30 @@ bool DocumentData::isStickerSetInstalled() const {
} }
Image *DocumentData::getReplyPreview(Data::FileOrigin origin) { Image *DocumentData::getReplyPreview(Data::FileOrigin origin) {
if (!_replyPreview->isNull() && !thumb->isNull()) { if (!_thumbnail) {
if (thumb->loaded()) { return nullptr;
int w = thumb->width(), h = thumb->height(); } else if (_replyPreview
if (w <= 0) w = 1; && (_replyPreview.good() || !_thumbnail->loaded())) {
if (h <= 0) h = 1; return _replyPreview.image();
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 option = isVideoMessage()
auto options = Images::Option::Smooth | (isVideoMessage() ? Images::Option::Circled : Images::Option::None) | Images::Option::TransparentBackground; ? Images::Option::Circled
auto outerSize = st::msgReplyBarSize.height(); : Images::Option::None;
auto image = thumb->pixNoCache(origin, thumbSize.width(), thumbSize.height(), options, outerSize, outerSize); if (_thumbnail->loaded()) {
_replyPreview = std::make_unique<Image>( _replyPreview.prepare(
std::make_unique<Images::ImageSource>( _thumbnail.get(),
image.toImage(), origin,
"PNG")); option);
} else { } else {
thumb->load(origin); _thumbnail->load(origin);
if (_thumbnailInline) {
_replyPreview.prepare(
_thumbnailInline.get(),
origin,
option | Images::Option::Blurred);
} }
} }
return _replyPreview.get(); return _replyPreview.image();
} }
StickerData *DocumentData::sticker() const { StickerData *DocumentData::sticker() const {
@ -1134,7 +1180,7 @@ StickerData *DocumentData::sticker() const {
: nullptr; : nullptr;
} }
void DocumentData::checkSticker() { void DocumentData::checkStickerLarge() {
const auto data = sticker(); const auto data = sticker();
if (!data) return; if (!data) return;
@ -1164,25 +1210,25 @@ void DocumentData::checkSticker() {
} }
} }
void DocumentData::checkStickerThumb() { void DocumentData::checkStickerSmall() {
if (hasGoodStickerThumb()) { if (thumbnailEnoughForSticker()) {
thumb->load(stickerSetOrigin()); _thumbnail->load(stickerSetOrigin());
} else { } else {
checkSticker(); checkStickerLarge();
} }
} }
Image *DocumentData::getStickerImage() { Image *DocumentData::getStickerLarge() {
checkSticker(); checkStickerLarge();
if (const auto data = sticker()) { if (const auto data = sticker()) {
return data->image.get(); return data->image.get();
} }
return nullptr; return nullptr;
} }
Image *DocumentData::getStickerThumb() { Image *DocumentData::getStickerSmall() {
if (hasGoodStickerThumb()) { if (thumbnailEnoughForSticker()) {
return thumb->isNull() ? nullptr : thumb.get(); return _thumbnail->isNull() ? nullptr : _thumbnail.get();
} else if (const auto data = sticker()) { } else if (const auto data = sticker()) {
return data->image.get(); return data->image.get();
} }
@ -1236,8 +1282,8 @@ bool DocumentData::hasWebLocation() const {
return _urlLocation.dc() != 0 && _urlLocation.accessHash() != 0; return _urlLocation.dc() != 0 && _urlLocation.accessHash() != 0;
} }
bool DocumentData::isValid() const { bool DocumentData::isNull() const {
return hasRemoteLocation() || hasWebLocation() || !_url.isEmpty(); return !hasRemoteLocation() && !hasWebLocation() && _url.isEmpty();
} }
MTPInputDocument DocumentData::mtpInput() const { MTPInputDocument DocumentData::mtpInput() const {
@ -1260,9 +1306,9 @@ void DocumentData::refreshFileReference(const QByteArray &value) {
void DocumentData::refreshStickerThumbFileReference() { void DocumentData::refreshStickerThumbFileReference() {
if (const auto data = sticker()) { if (const auto data = sticker()) {
if (thumb->loading()) { if (_thumbnail->loading()) {
data->loc.refreshFileReference( data->loc.refreshFileReference(
thumb->location().fileReference()); _thumbnail->location().fileReference());
} }
} }
} }
@ -1407,9 +1453,9 @@ void DocumentData::recountIsImage() {
&& fileIsImage(filename(), mimeString()); && fileIsImage(filename(), mimeString());
} }
bool DocumentData::hasGoodStickerThumb() const { bool DocumentData::thumbnailEnoughForSticker() const {
return !thumb->isNull() return !_thumbnail->isNull()
&& ((thumb->width() >= 128) || (thumb->height() >= 128)); && ((_thumbnail->width() >= 128) || (_thumbnail->height() >= 128));
} }
void DocumentData::setRemoteLocation( void DocumentData::setRemoteLocation(
@ -1420,7 +1466,7 @@ void DocumentData::setRemoteLocation(
if (_dc != dc || _access != access) { if (_dc != dc || _access != access) {
_dc = dc; _dc = dc;
_access = access; _access = access;
if (isValid()) { if (!isNull()) {
if (_location.check()) { if (_location.check()) {
Local::writeFileLocation(mediaKey(), _location); Local::writeFileLocation(mediaKey(), _location);
} else { } else {
@ -1439,10 +1485,12 @@ void DocumentData::setWebLocation(const WebFileLocation &location) {
_urlLocation = location; _urlLocation = location;
} }
void DocumentData::collectLocalData(DocumentData *local) { void DocumentData::collectLocalData(not_null<DocumentData*> local) {
if (local == this) return; if (local == this) {
return;
}
_session->data().cache().copyIfEmpty(local->cacheKey(), cacheKey()); _owner->cache().copyIfEmpty(local->cacheKey(), cacheKey());
if (!local->_data.isEmpty()) { if (!local->_data.isEmpty()) {
ActiveCache().decrement(_data.size()); ActiveCache().decrement(_data.size());
_data = local->_data; _data = local->_data;

View File

@ -8,6 +8,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#pragma once #pragma once
#include "data/data_types.h" #include "data/data_types.h"
#include "ui/image/image.h"
namespace Images { namespace Images {
class Source; class Source;
@ -19,6 +20,10 @@ struct Key;
} // namespace Cache } // namespace Cache
} // namespace Storage } // namespace Storage
namespace Data {
class Session;
} // namespace Data
class AuthSession; class AuthSession;
class mtpFileLoader; class mtpFileLoader;
@ -75,9 +80,10 @@ class Document;
class DocumentData { class DocumentData {
public: 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( void setattributes(
const QVector<MTPDocumentAttribute> &attributes); const QVector<MTPDocumentAttribute> &attributes);
@ -93,11 +99,11 @@ public:
FilePathResolveSaveFromData, FilePathResolveSaveFromData,
FilePathResolveSaveFromDataSilent, FilePathResolveSaveFromDataSilent,
}; };
bool loaded( [[nodiscard]] bool loaded(
FilePathResolveType type = FilePathResolveCached) const; FilePathResolveType type = FilePathResolveCached) const;
bool loading() const; [[nodiscard]] bool loading() const;
QString loadingFilePath() const; [[nodiscard]] QString loadingFilePath() const;
bool displayLoading() const; [[nodiscard]] bool displayLoading() const;
void save( void save(
Data::FileOrigin origin, Data::FileOrigin origin,
const QString &toFile, const QString &toFile,
@ -106,19 +112,19 @@ public:
LoadFromCloudSetting fromCloud = LoadFromCloudOrLocal, LoadFromCloudSetting fromCloud = LoadFromCloudOrLocal,
bool autoLoading = false); bool autoLoading = false);
void cancel(); void cancel();
bool cancelled() const; [[nodiscard]] bool cancelled() const;
float64 progress() const; [[nodiscard]] float64 progress() const;
int32 loadOffset() const; [[nodiscard]] int32 loadOffset() const;
bool uploading() const; [[nodiscard]] bool uploading() const;
void setWaitingForAlbum(); void setWaitingForAlbum();
bool waitingForAlbum() const; [[nodiscard]] bool waitingForAlbum() const;
QByteArray data() const; [[nodiscard]] QByteArray data() const;
const FileLocation &location(bool check = false) const; [[nodiscard]] const FileLocation &location(bool check = false) const;
void setLocation(const FileLocation &loc); void setLocation(const FileLocation &loc);
QString filepath( [[nodiscard]] QString filepath(
FilePathResolveType type = FilePathResolveCached, FilePathResolveType type = FilePathResolveCached,
bool forceSavingAs = false) const; bool forceSavingAs = false) const;
@ -127,44 +133,50 @@ public:
void performActionOnLoad(); void performActionOnLoad();
void unload(); void unload();
Image *getReplyPreview(Data::FileOrigin origin); [[nodiscard]] Image *getReplyPreview(Data::FileOrigin origin);
StickerData *sticker() const; [[nodiscard]] StickerData *sticker() const;
void checkSticker(); void checkStickerLarge();
void checkStickerThumb(); void checkStickerSmall();
Image *getStickerThumb(); [[nodiscard]] Image *getStickerSmall();
Image *getStickerImage(); [[nodiscard]] Image *getStickerLarge();
Data::FileOrigin stickerSetOrigin() const; [[nodiscard]] Data::FileOrigin stickerSetOrigin() const;
Data::FileOrigin stickerOrGifOrigin() const; [[nodiscard]] Data::FileOrigin stickerOrGifOrigin() const;
bool isStickerSetInstalled() const; [[nodiscard]] bool isStickerSetInstalled() const;
SongData *song(); [[nodiscard]] SongData *song();
const SongData *song() const; [[nodiscard]] const SongData *song() const;
VoiceData *voice(); [[nodiscard]] VoiceData *voice();
const VoiceData *voice() const; [[nodiscard]] const VoiceData *voice() const;
bool isVoiceMessage() const; [[nodiscard]] bool isVoiceMessage() const;
bool isVideoMessage() const; [[nodiscard]] bool isVideoMessage() const;
bool isSong() const; [[nodiscard]] bool isSong() const;
bool isAudioFile() const; [[nodiscard]] bool isAudioFile() const;
bool isVideoFile() const; [[nodiscard]] bool isVideoFile() const;
bool isAnimation() const; [[nodiscard]] bool isAnimation() const;
bool isGifv() const; [[nodiscard]] bool isGifv() const;
bool isTheme() const; [[nodiscard]] bool isTheme() const;
bool isSharedMediaMusic() const; [[nodiscard]] bool isSharedMediaMusic() const;
int32 duration() const; [[nodiscard]] int32 duration() const;
bool isImage() const; [[nodiscard]] bool isImage() const;
void recountIsImage(); void recountIsImage();
bool supportsStreaming() const; [[nodiscard]] bool supportsStreaming() const;
void setData(const QByteArray &data) { void setData(const QByteArray &data) {
_data = data; _data = data;
} }
bool checkWallPaperProperties(); 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; [[nodiscard]] Image *goodThumbnail() const;
Storage::Cache::Key goodThumbnailCacheKey() const; [[nodiscard]] Storage::Cache::Key goodThumbnailCacheKey() const;
void setGoodThumbnail(QImage &&image, QByteArray &&bytes); void setGoodThumbnail(QImage &&image, QByteArray &&bytes);
void refreshGoodThumbnail(); void refreshGoodThumbnail();
void replaceGoodThumbnail(std::unique_ptr<Images::Source> &&source); void replaceGoodThumbnail(std::unique_ptr<Images::Source> &&source);
@ -175,11 +187,11 @@ public:
const QByteArray &fileReference); const QByteArray &fileReference);
void setContentUrl(const QString &url); void setContentUrl(const QString &url);
void setWebLocation(const WebFileLocation &location); void setWebLocation(const WebFileLocation &location);
bool hasRemoteLocation() const; [[nodiscard]] bool hasRemoteLocation() const;
bool hasWebLocation() const; [[nodiscard]] bool hasWebLocation() const;
bool isValid() const; [[nodiscard]] bool isNull() const;
MTPInputDocument mtpInput() const; [[nodiscard]] MTPInputDocument mtpInput() const;
QByteArray fileReference() const; [[nodiscard]] QByteArray fileReference() const;
void refreshFileReference(const QByteArray &value); void refreshFileReference(const QByteArray &value);
void refreshStickerThumbFileReference(); void refreshStickerThumbFileReference();
@ -187,22 +199,22 @@ public:
// (for example for displaying an external inline bot result) // (for example for displaying an external inline bot result)
// and it has downloaded data, we can collect that data from it // and it has downloaded data, we can collect that data from it
// to (this) received from the server "same" document. // to (this) received from the server "same" document.
void collectLocalData(DocumentData *local); void collectLocalData(not_null<DocumentData*> local);
QString filename() const; [[nodiscard]] QString filename() const;
QString mimeString() const; [[nodiscard]] QString mimeString() const;
bool hasMimeType(QLatin1String mime) const; [[nodiscard]] bool hasMimeType(QLatin1String mime) const;
void setMimeString(const QString &mime); void setMimeString(const QString &mime);
MediaKey mediaKey() const; [[nodiscard]] MediaKey mediaKey() const;
Storage::Cache::Key cacheKey() const; [[nodiscard]] Storage::Cache::Key cacheKey() const;
uint8 cacheTag() const; [[nodiscard]] uint8 cacheTag() const;
static QString ComposeNameString( [[nodiscard]] static QString ComposeNameString(
const QString &filename, const QString &filename,
const QString &songTitle, const QString &songTitle,
const QString &songPerformer); const QString &songPerformer);
QString composeNameString() const; [[nodiscard]] QString composeNameString() const;
~DocumentData(); ~DocumentData();
@ -210,7 +222,6 @@ public:
DocumentType type = FileDocument; DocumentType type = FileDocument;
QSize dimensions; QSize dimensions;
int32 date = 0; int32 date = 0;
ImagePtr thumb;
int32 size = 0; int32 size = 0;
FileStatus status = FileReady; FileStatus status = FileReady;
@ -225,6 +236,8 @@ private:
void destroyLoader(mtpFileLoader *newValue = nullptr) const; void destroyLoader(mtpFileLoader *newValue = nullptr) const;
[[nodiscard]] bool thumbnailEnoughForSticker() const;
// Two types of location: from MTProto by dc+access or from web by url // Two types of location: from MTProto by dc+access or from web by url
int32 _dc = 0; int32 _dc = 0;
uint64 _access = 0; uint64 _access = 0;
@ -234,10 +247,12 @@ private:
QString _mimeString; QString _mimeString;
WebFileLocation _urlLocation; WebFileLocation _urlLocation;
ImagePtr _thumbnailInline;
ImagePtr _thumbnail;
std::unique_ptr<Image> _goodThumbnail; std::unique_ptr<Image> _goodThumbnail;
std::unique_ptr<Image> _replyPreview; Data::ReplyPreview _replyPreview;
not_null<AuthSession*> _session; not_null<Data::Session*> _owner;
FileLocation _location; FileLocation _location;
QByteArray _data; QByteArray _data;

View File

@ -290,7 +290,7 @@ bool MediaPhoto::canBeGrouped() const {
} }
bool MediaPhoto::hasReplyPreview() const { bool MediaPhoto::hasReplyPreview() const {
return !_photo->thumb->isNull(); return !_photo->isNull();
} }
Image *MediaPhoto::replyPreview() const { Image *MediaPhoto::replyPreview() const {
@ -378,7 +378,7 @@ bool MediaPhoto::updateSentMedia(const MTPMessageMedia &media) {
QByteArray bytes; QByteArray bytes;
}; };
const auto saveImageToCache = [&]( const auto saveImageToCache = [&](
const ImagePtr &image, not_null<Image*> image,
SizeData size) { SizeData size) {
Expects(size.location != nullptr); Expects(size.location != nullptr);
@ -435,9 +435,9 @@ bool MediaPhoto::updateSentMedia(const MTPMessageMedia &media) {
continue; continue;
} }
if (size.letter == 's') { if (size.letter == 's') {
saveImageToCache(_photo->thumb, size); saveImageToCache(_photo->thumbnailSmall(), size);
} else if (size.letter == 'm') { } else if (size.letter == 'm') {
saveImageToCache(_photo->medium, size); saveImageToCache(_photo->thumbnail(), size);
} else if (size.letter == 'x' && max < 1) { } else if (size.letter == 'x' && max < 1) {
max = 1; max = 1;
maxSize = size; maxSize = size;
@ -450,7 +450,7 @@ bool MediaPhoto::updateSentMedia(const MTPMessageMedia &media) {
} }
} }
if (maxSize.location) { if (maxSize.location) {
saveImageToCache(_photo->full, maxSize); saveImageToCache(_photo->large(), maxSize);
} }
return true; return true;
} }
@ -533,7 +533,7 @@ bool MediaFile::canBeGrouped() const {
} }
bool MediaFile::hasReplyPreview() const { bool MediaFile::hasReplyPreview() const {
return !_document->thumb->isNull(); return _document->hasThumbnail();
} }
Image *MediaFile::replyPreview() const { Image *MediaFile::replyPreview() const {
@ -1017,9 +1017,9 @@ WebPageData *MediaWebPage::webpage() const {
bool MediaWebPage::hasReplyPreview() const { bool MediaWebPage::hasReplyPreview() const {
if (const auto document = MediaWebPage::document()) { if (const auto document = MediaWebPage::document()) {
return !document->thumb->isNull(); return document->hasThumbnail();
} else if (const auto photo = MediaWebPage::photo()) { } else if (const auto photo = MediaWebPage::photo()) {
return !photo->thumb->isNull(); return !photo->isNull();
} }
return false; return false;
} }
@ -1080,9 +1080,9 @@ std::unique_ptr<Media> MediaGame::clone(not_null<HistoryItem*> parent) {
bool MediaGame::hasReplyPreview() const { bool MediaGame::hasReplyPreview() const {
if (const auto document = _game->document) { if (const auto document = _game->document) {
return !document->thumb->isNull(); return document->hasThumbnail();
} else if (const auto photo = _game->photo) { } else if (const auto photo = _game->photo) {
return !photo->thumb->isNull(); return !photo->isNull();
} }
return false; return false;
} }
@ -1182,7 +1182,7 @@ const Invoice *MediaInvoice::invoice() const {
bool MediaInvoice::hasReplyPreview() const { bool MediaInvoice::hasReplyPreview() const {
if (const auto photo = _invoice.photo) { if (const auto photo = _invoice.photo) {
return !photo->thumb->isNull(); return !photo->isNull();
} }
return false; return false;
} }

View File

@ -12,49 +12,41 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "ui/image/image.h" #include "ui/image/image.h"
#include "ui/image/image_source.h" #include "ui/image/image_source.h"
#include "mainwidget.h" #include "mainwidget.h"
#include "auth_session.h"
#include "core/application.h" #include "core/application.h"
PhotoData::PhotoData(const PhotoId &id) PhotoData::PhotoData(not_null<Data::Session*> owner, PhotoId id)
: id(id) { : id(id)
, _owner(owner) {
} }
PhotoData::PhotoData( Data::Session &PhotoData::owner() const {
const PhotoId &id, return *_owner;
const uint64 &access, }
const QByteArray &fileReference,
TimeId date, AuthSession &PhotoData::session() const {
const ImagePtr &thumb, return _owner->session();
const ImagePtr &medium,
const ImagePtr &full)
: id(id)
, access(access)
, date(date)
, thumb(thumb)
, medium(medium)
, full(full) {
} }
void PhotoData::automaticLoad( void PhotoData::automaticLoad(
Data::FileOrigin origin, Data::FileOrigin origin,
const HistoryItem *item) { const HistoryItem *item) {
full->automaticLoad(origin, item); _large->automaticLoad(origin, item);
} }
void PhotoData::automaticLoadSettingsChanged() { void PhotoData::automaticLoadSettingsChanged() {
full->automaticLoadSettingsChanged(); _large->automaticLoadSettingsChanged();
} }
void PhotoData::download(Data::FileOrigin origin) { void PhotoData::download(Data::FileOrigin origin) {
full->loadEvenCancelled(origin); _large->loadEvenCancelled(origin);
Auth().data().notifyPhotoLayoutChanged(this); _owner->notifyPhotoLayoutChanged(this);
} }
bool PhotoData::loaded() const { bool PhotoData::loaded() const {
bool wasLoading = loading(); bool wasLoading = loading();
if (full->loaded()) { if (_large->loaded()) {
if (wasLoading) { if (wasLoading) {
Auth().data().notifyPhotoLayoutChanged(this); _owner->notifyPhotoLayoutChanged(this);
} }
return true; return true;
} }
@ -62,18 +54,18 @@ bool PhotoData::loaded() const {
} }
bool PhotoData::loading() const { bool PhotoData::loading() const {
return full->loading(); return _large->loading();
} }
bool PhotoData::displayLoading() const { bool PhotoData::displayLoading() const {
return full->loading() return _large->loading()
? full->displayLoading() ? _large->displayLoading()
: (uploading() && !waitingForAlbum()); : (uploading() && !waitingForAlbum());
} }
void PhotoData::cancel() { void PhotoData::cancel() {
full->cancel(); _large->cancel();
Auth().data().notifyPhotoLayoutChanged(this); _owner->notifyPhotoLayoutChanged(this);
} }
float64 PhotoData::progress() const { float64 PhotoData::progress() const {
@ -83,7 +75,7 @@ float64 PhotoData::progress() const {
} }
return 0; return 0;
} }
return full->progress(); return _large->progress();
} }
void PhotoData::setWaitingForAlbum() { void PhotoData::setWaitingForAlbum() {
@ -97,7 +89,7 @@ bool PhotoData::waitingForAlbum() const {
} }
int32 PhotoData::loadOffset() const { int32 PhotoData::loadOffset() const {
return full->loadOffset(); return _large->loadOffset();
} }
bool PhotoData::uploading() const { bool PhotoData::uploading() const {
@ -105,43 +97,42 @@ bool PhotoData::uploading() const {
} }
void PhotoData::unload() { void PhotoData::unload() {
// Forget thumb only when image cache limit exceeds. // Forget thumbnail only when image cache limit exceeds.
//thumb->unload(); //_thumbnailInline->unload();
medium->unload(); _thumbnailSmall->unload();
full->unload(); _thumbnail->unload();
_replyPreview = nullptr; _large->unload();
_replyPreview.clear();
} }
Image *PhotoData::getReplyPreview(Data::FileOrigin origin) { Image *PhotoData::getReplyPreview(Data::FileOrigin origin) {
if (!_replyPreview && !thumb->isNull()) { if (_replyPreview
const auto previewFromImage = [&](const ImagePtr &image) { && (_replyPreview.good() || !_thumbnailSmall->loaded())) {
if (!image->loaded()) { return _replyPreview.image();
image->load(origin); }
return std::unique_ptr<Image>(); if (_thumbnailSmall->isDelayedStorageImage()
} && !_large->isNull()
int w = image->width(), h = image->height(); && !_large->isDelayedStorageImage()
if (w <= 0) w = 1; && _large->loaded()) {
if (h <= 0) h = 1; _replyPreview.prepare(
return std::make_unique<Image>( _large.get(),
std::make_unique<Images::ImageSource>( origin,
(w > h Images::Option(0));
? image->pix( } else if (_thumbnailSmall->loaded()) {
origin, _replyPreview.prepare(
w * st::msgReplyBarSize.height() / h, _thumbnailSmall.get(),
st::msgReplyBarSize.height()) origin,
: image->pix(origin, st::msgReplyBarSize.height()) Images::Option(0));
).toImage(), } else {
"PNG")); _thumbnailSmall->load(origin);
}; if (_thumbnailInline) {
if (thumb->isDelayedStorageImage() _replyPreview.prepare(
&& !full->isNull() _thumbnailInline.get(),
&& !full->isDelayedStorageImage()) { origin,
_replyPreview = previewFromImage(full); Images::Option::Blurred);
} else {
_replyPreview = previewFromImage(thumb);
} }
} }
return _replyPreview.get(); return _replyPreview.image();
} }
MTPInputPhoto PhotoData::mtpInput() const { MTPInputPhoto PhotoData::mtpInput() const {
@ -151,19 +142,88 @@ MTPInputPhoto PhotoData::mtpInput() const {
MTP_bytes(fileReference)); MTP_bytes(fileReference));
} }
void PhotoData::collectLocalData(PhotoData *local) { void PhotoData::collectLocalData(not_null<PhotoData*> local) {
if (local == this) return; 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 from = src->cacheKey()) {
if (const auto to = dst->cacheKey()) { if (const auto to = dst->cacheKey()) {
Auth().data().cache().copyIfEmpty(*from, *to); _owner->cache().copyIfEmpty(*from, *to);
} }
} }
}; };
copyImage(local->thumb, thumb); copyImage(local->_thumbnailSmall, _thumbnailSmall);
copyImage(local->medium, medium); copyImage(local->_thumbnail, _thumbnail);
copyImage(local->full, full); 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 { void PhotoOpenClickHandler::onClickImpl() const {

View File

@ -9,17 +9,18 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "data/data_types.h" #include "data/data_types.h"
class AuthSession;
namespace Data {
class Session;
} // namespace Data
class PhotoData { class PhotoData {
public: public:
explicit PhotoData(const PhotoId &id); PhotoData(not_null<Data::Session*> owner, PhotoId id);
PhotoData(
const PhotoId &id, [[nodiscard]] Data::Session &owner() const;
const uint64 &access, [[nodiscard]] AuthSession &session() const;
const QByteArray &fileReference,
TimeId date,
const ImagePtr &thumb,
const ImagePtr &medium,
const ImagePtr &full);
void automaticLoad( void automaticLoad(
Data::FileOrigin origin, Data::FileOrigin origin,
@ -27,35 +28,53 @@ public:
void automaticLoadSettingsChanged(); void automaticLoadSettingsChanged();
void download(Data::FileOrigin origin); void download(Data::FileOrigin origin);
bool loaded() const; [[nodiscard]] bool loaded() const;
bool loading() const; [[nodiscard]] bool loading() const;
bool displayLoading() const; [[nodiscard]] bool displayLoading() const;
void cancel(); void cancel();
float64 progress() const; [[nodiscard]] float64 progress() const;
int32 loadOffset() const; [[nodiscard]] int32 loadOffset() const;
bool uploading() const; [[nodiscard]] bool uploading() const;
void setWaitingForAlbum(); void setWaitingForAlbum();
bool waitingForAlbum() const; [[nodiscard]] bool waitingForAlbum() const;
void unload(); 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 // When we have some client-side generated photo
// (for example for displaying an external inline bot result) // (for example for displaying an external inline bot result)
// and it has downloaded full image, we can collect image from it // and it has downloaded full image, we can collect image from it
// to (this) received from the server "same" photo. // 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; PhotoId id = 0;
uint64 access = 0; uint64 access = 0;
QByteArray fileReference; QByteArray fileReference;
TimeId date = 0; TimeId date = 0;
ImagePtr thumb;
ImagePtr medium;
ImagePtr full;
PeerData *peer = nullptr; // for chat and channel photos connection PeerData *peer = nullptr; // for chat and channel photos connection
// geo, caption // geo, caption
@ -63,7 +82,14 @@ public:
std::unique_ptr<Data::UploadState> uploadingData; std::unique_ptr<Data::UploadState> uploadingData;
private: private:
std::unique_ptr<Image> _replyPreview; ImagePtr _thumbnailInline;
ImagePtr _thumbnailSmall;
ImagePtr _thumbnail;
ImagePtr _large;
Data::ReplyPreview _replyPreview;
not_null<Data::Session*> _owner;
}; };

View File

@ -57,23 +57,10 @@ using ViewElement = HistoryView::Element;
// b: crop 320x320 // b: crop 320x320
// c: crop 640x640 // c: crop 640x640
// d: crop 1280x1280 // d: crop 1280x1280
const auto ThumbLevels = QByteArray::fromRawData("isambcxydw", 10); const auto InlineLevels = QByteArray::fromRawData("i", 1);
const auto MediumLevels = QByteArray::fromRawData("mbcxasydwi", 10); const auto SmallLevels = QByteArray::fromRawData("sambcxydwi", 10);
const auto FullLevels = QByteArray::fromRawData("yxwmsdcbai", 10); const auto ThumbnailLevels = QByteArray::fromRawData("mbcxasydwi", 10);
const auto LargeLevels = 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);
}
}
}
void CheckForSwitchInlineButton(not_null<HistoryItem*> item) { void CheckForSwitchInlineButton(not_null<HistoryItem*> item) {
if (item->out() || !item->hasSwitchInlineButton()) { if (item->out() || !item->hasSwitchInlineButton()) {
@ -121,22 +108,31 @@ QString ExtractUnavailableReason(const QString &restriction) {
return QString(); 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) { 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 size.match([](const MTPDphotoSizeEmpty &) {
return kInvalid; return kInvalid;
}, [](const MTPDphotoStrippedSize &) { }, [](const MTPDphotoStrippedSize &) {
return kInvalid; return kInvalid;
}, [](const auto &data) { }, [](const auto &data) {
return (data.vw.v >= 90 || data.vh.v >= 90) return (data.vw.v * data.vh.v);
? (data.vw.v * data.vh.v)
: kInvalid;
}); });
}; };
const auto &thumbs = data.vthumbs.v; const auto &thumbs = data.vthumbs.v;
const auto i = ranges::max_element(thumbs, std::greater<>(), area); const auto i = ranges::max_element(thumbs, std::less<>(), area);
return (i != thumbs.end()) return (i != thumbs.end() && area(*i) > 0)
? (*i) ? (*i)
: MTPPhotoSize(MTP_photoSizeEmpty(MTP_string(""))); : MTPPhotoSize(MTP_photoSizeEmpty(MTP_string("")));
} }
@ -1609,20 +1605,19 @@ void Session::checkSelfDestructItems() {
not_null<PhotoData*> Session::photo(PhotoId id) { not_null<PhotoData*> Session::photo(PhotoId id) {
auto i = _photos.find(id); auto i = _photos.find(id);
if (i == _photos.end()) { 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(); return i->second.get();
} }
not_null<PhotoData*> Session::processPhoto(const MTPPhoto &data) { not_null<PhotoData*> Session::processPhoto(const MTPPhoto &data) {
switch (data.type()) { return data.match([&](const MTPDphoto &data) {
case mtpc_photo: return processPhoto(data);
return processPhoto(data.c_photo()); }, [&](const MTPDphotoEmpty &data) {
return photo(data.vid.v);
case mtpc_photoEmpty: });
return photo(data.c_photoEmpty().vid.v);
}
Unexpected("Type in Session::photo().");
} }
not_null<PhotoData*> Session::processPhoto(const MTPDphoto &data) { 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( not_null<PhotoData*> Session::processPhoto(
const MTPPhoto &data, const MTPPhoto &data,
const PreparedPhotoThumbs &thumbs) { const PreparedPhotoThumbs &thumbs) {
auto thumb = (const QImage*)nullptr; Expects(!thumbs.empty());
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"));
case mtpc_photoEmpty: const auto find = [&](const QByteArray &levels) {
return photo(data.c_photoEmpty().vid.v); const auto kInvalidIndex = int(levels.size());
} const auto level = [&](const auto &pair) {
Unexpected("Type in Session::photo() with prepared thumbs."); 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( not_null<PhotoData*> Session::photo(
@ -1685,31 +1674,29 @@ not_null<PhotoData*> Session::photo(
const uint64 &access, const uint64 &access,
const QByteArray &fileReference, const QByteArray &fileReference,
TimeId date, TimeId date,
const ImagePtr &thumb, const ImagePtr &thumbnailInline,
const ImagePtr &medium, const ImagePtr &thumbnailSmall,
const ImagePtr &full) { const ImagePtr &thumbnail,
const ImagePtr &large) {
const auto result = photo(id); const auto result = photo(id);
photoApplyFields( photoApplyFields(
result, result,
access, access,
fileReference, fileReference,
date, date,
thumb, thumbnailInline,
medium, thumbnailSmall,
full); thumbnail,
large);
return result; return result;
} }
void Session::photoConvert( void Session::photoConvert(
not_null<PhotoData*> original, not_null<PhotoData*> original,
const MTPPhoto &data) { const MTPPhoto &data) {
const auto id = [&] { const auto id = data.match([](const auto &data) {
switch (data.type()) { return data.vid.v;
case mtpc_photo: return data.c_photo().vid.v; });
case mtpc_photoEmpty: return data.c_photoEmpty().vid.v;
}
Unexpected("Type in Session::photoConvert().");
}();
if (original->id != id) { if (original->id != id) {
auto i = _photos.find(id); auto i = _photos.find(id);
if (i == _photos.end()) { if (i == _photos.end()) {
@ -1732,23 +1719,26 @@ void Session::photoConvert(
PhotoData *Session::photoFromWeb( PhotoData *Session::photoFromWeb(
const MTPWebDocument &data, const MTPWebDocument &data,
ImagePtr thumb, ImagePtr thumbnailSmall,
bool willBecomeNormal) { bool willBecomeNormal) {
const auto full = Images::Create(data); const auto large = Images::Create(data);
if (full->isNull()) { const auto thumbnailInline = ImagePtr();
if (large->isNull()) {
return nullptr; return nullptr;
} }
auto medium = ImagePtr(); auto thumbnail = large;
if (willBecomeNormal) { if (willBecomeNormal) {
const auto width = full->width(); const auto width = large->width();
const auto height = full->height(); const auto height = large->height();
if (thumb->isNull()) { if (thumbnailSmall->isNull()) {
auto thumbsize = shrinkToKeepAspect(width, height, 100, 100); 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); 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( return photo(
@ -1756,9 +1746,10 @@ PhotoData *Session::photoFromWeb(
uint64(0), uint64(0),
QByteArray(), QByteArray(),
unixtime(), unixtime(),
thumb, thumbnailInline,
medium, thumbnailSmall,
full); thumbnail,
large);
} }
void Session::photoApplyFields( void Session::photoApplyFields(
@ -1772,48 +1763,42 @@ void Session::photoApplyFields(
void Session::photoApplyFields( void Session::photoApplyFields(
not_null<PhotoData*> photo, not_null<PhotoData*> photo,
const MTPDphoto &data) { const MTPDphoto &data) {
auto thumb = (const MTPPhotoSize*)nullptr; const auto &sizes = data.vsizes.v;
auto medium = (const MTPPhotoSize*)nullptr; const auto find = [&](const QByteArray &levels) {
auto full = (const MTPPhotoSize*)nullptr; const auto kInvalidIndex = int(levels.size());
auto thumbLevel = -1; const auto level = [&](const MTPPhotoSize &size) {
auto mediumLevel = -1; const auto letter = size.match([](const MTPDphotoSizeEmpty &) {
auto fullLevel = -1; return char(0);
for (const auto &sizeData : data.vsizes.v) { }, [](const auto &size) {
const auto sizeLetter = sizeData.match([](MTPDphotoSizeEmpty) { return size.vtype.v.isEmpty() ? char(0) : size.vtype.v[0];
return char(0); });
}, [](const auto &size) { const auto index = levels.indexOf(letter);
return size.vtype.v.isEmpty() ? char(0) : size.vtype.v[0]; return (index >= 0) ? index : kInvalidIndex;
}); };
if (!sizeLetter) continue; const auto result = ranges::max_element(
sizes,
const auto newThumbLevel = ThumbLevels.indexOf(sizeLetter); std::greater<>(),
const auto newMediumLevel = MediumLevels.indexOf(sizeLetter); level);
const auto newFullLevel = FullLevels.indexOf(sizeLetter); return (level(*result) == kInvalidIndex) ? sizes.end() : result;
if (newThumbLevel < 0 || newMediumLevel < 0 || newFullLevel < 0) { };
continue; const auto image = [&](const QByteArray &levels) {
} const auto i = find(levels);
if (thumbLevel < 0 || newThumbLevel < thumbLevel) { return (i == sizes.end()) ? ImagePtr() : App::image(*i);
thumbLevel = newThumbLevel; };
thumb = &sizeData; const auto thumbnailInline = image(InlineLevels);
} const auto thumbnailSmall = image(SmallLevels);
if (mediumLevel < 0 || newMediumLevel < mediumLevel) { const auto thumbnail = image(ThumbnailLevels);
mediumLevel = newMediumLevel; const auto large = image(LargeLevels);
medium = &sizeData; if (thumbnailSmall && thumbnail && large) {
}
if (fullLevel < 0 || newFullLevel < fullLevel) {
fullLevel = newFullLevel;
full = &sizeData;
}
}
if (thumb && medium && full) {
photoApplyFields( photoApplyFields(
photo, photo,
data.vaccess_hash.v, data.vaccess_hash.v,
data.vfile_reference.v, data.vfile_reference.v,
data.vdate.v, data.vdate.v,
App::image(*thumb), thumbnailInline,
App::image(*medium), thumbnailSmall,
App::image(*full)); thumbnail,
large);
} }
} }
@ -1822,18 +1807,21 @@ void Session::photoApplyFields(
const uint64 &access, const uint64 &access,
const QByteArray &fileReference, const QByteArray &fileReference,
TimeId date, TimeId date,
const ImagePtr &thumb, const ImagePtr &thumbnailInline,
const ImagePtr &medium, const ImagePtr &thumbnailSmall,
const ImagePtr &full) { const ImagePtr &thumbnail,
const ImagePtr &large) {
if (!date) { if (!date) {
return; return;
} }
photo->access = access; photo->access = access;
photo->fileReference = fileReference; photo->fileReference = fileReference;
photo->date = date; photo->date = date;
UpdateImage(photo->thumb, thumb); photo->updateImages(
UpdateImage(photo->medium, medium); thumbnailInline,
UpdateImage(photo->full, full); thumbnailSmall,
thumbnail,
large);
} }
not_null<DocumentData*> Session::document(DocumentId id) { not_null<DocumentData*> Session::document(DocumentId id) {
@ -1841,7 +1829,7 @@ not_null<DocumentData*> Session::document(DocumentId id) {
if (i == _documents.cend()) { if (i == _documents.cend()) {
i = _documents.emplace( i = _documents.emplace(
id, id,
std::make_unique<DocumentData>(id, _session)).first; std::make_unique<DocumentData>(this, id)).first;
} }
return i->second.get(); return i->second.get();
} }
@ -1879,6 +1867,7 @@ not_null<DocumentData*> Session::processDocument(
fields.vdate.v, fields.vdate.v,
fields.vattributes.v, fields.vattributes.v,
qs(fields.vmime_type), qs(fields.vmime_type),
ImagePtr(),
Images::Create(std::move(thumb), "JPG"), Images::Create(std::move(thumb), "JPG"),
fields.vdc_id.v, fields.vdc_id.v,
fields.vsize.v, fields.vsize.v,
@ -1895,7 +1884,8 @@ not_null<DocumentData*> Session::document(
TimeId date, TimeId date,
const QVector<MTPDocumentAttribute> &attributes, const QVector<MTPDocumentAttribute> &attributes,
const QString &mime, const QString &mime,
const ImagePtr &thumb, const ImagePtr &thumbnailInline,
const ImagePtr &thumbnail,
int32 dc, int32 dc,
int32 size, int32 size,
const StorageImageLocation &thumbLocation) { const StorageImageLocation &thumbLocation) {
@ -1907,7 +1897,8 @@ not_null<DocumentData*> Session::document(
date, date,
attributes, attributes,
mime, mime,
thumb, thumbnailInline,
thumbnail,
dc, dc,
size, size,
thumbLocation); thumbLocation);
@ -1979,6 +1970,7 @@ DocumentData *Session::documentFromWeb(
unixtime(), unixtime(),
data.vattributes.v, data.vattributes.v,
data.vmime_type.v, data.vmime_type.v,
ImagePtr(),
thumb, thumb,
MTP::maindc(), MTP::maindc(),
int32(0), // data.vsize.v int32(0), // data.vsize.v
@ -2000,6 +1992,7 @@ DocumentData *Session::documentFromWeb(
unixtime(), unixtime(),
data.vattributes.v, data.vattributes.v,
data.vmime_type.v, data.vmime_type.v,
ImagePtr(),
thumb, thumb,
MTP::maindc(), MTP::maindc(),
int32(0), // data.vsize.v int32(0), // data.vsize.v
@ -2019,7 +2012,8 @@ void Session::documentApplyFields(
void Session::documentApplyFields( void Session::documentApplyFields(
not_null<DocumentData*> document, not_null<DocumentData*> document,
const MTPDdocument &data) { const MTPDdocument &data) {
const auto thumb = FindDocumentThumb(data); const auto thumbnailInline = FindDocumentInlineThumbnail(data);
const auto thumbnail = FindDocumentThumbnail(data);
documentApplyFields( documentApplyFields(
document, document,
data.vaccess_hash.v, data.vaccess_hash.v,
@ -2027,10 +2021,11 @@ void Session::documentApplyFields(
data.vdate.v, data.vdate.v,
data.vattributes.v, data.vattributes.v,
qs(data.vmime_type), qs(data.vmime_type),
App::image(thumb), App::image(thumbnailInline),
App::image(thumbnail),
data.vdc_id.v, data.vdc_id.v,
data.vsize.v, data.vsize.v,
StorageImageLocation::FromMTP(thumb)); StorageImageLocation::FromMTP(thumbnail));
} }
void Session::documentApplyFields( void Session::documentApplyFields(
@ -2040,7 +2035,8 @@ void Session::documentApplyFields(
TimeId date, TimeId date,
const QVector<MTPDocumentAttribute> &attributes, const QVector<MTPDocumentAttribute> &attributes,
const QString &mime, const QString &mime,
const ImagePtr &thumb, const ImagePtr &thumbnailInline,
const ImagePtr &thumbnail,
int32 dc, int32 dc,
int32 size, int32 size,
const StorageImageLocation &thumbLocation) { const StorageImageLocation &thumbLocation) {
@ -2053,13 +2049,7 @@ void Session::documentApplyFields(
} }
document->date = date; document->date = date;
document->setMimeString(mime); document->setMimeString(mime);
if (!thumb->isNull() document->updateThumbnails(thumbnailInline, thumbnail);
&& (document->thumb->isNull()
|| (document->sticker()
&& (document->thumb->width() < thumb->width()
|| document->thumb->height() < thumb->height())))) {
document->thumb = thumb;
}
document->size = size; document->size = size;
document->recountIsImage(); document->recountIsImage();
if (document->sticker() if (document->sticker()
@ -3085,7 +3075,7 @@ void Session::setWallpapers(const QVector<MTPWallPaper> &data, int32 hash) {
0ULL, // access_hash 0ULL, // access_hash
MTPDwallPaper::Flags(0), MTPDwallPaper::Flags(0),
QString(), // slug QString(), // slug
defaultBackground defaultBackground.get()
}); });
} }
const auto oldBackground = Images::Create( const auto oldBackground = Images::Create(
@ -3097,7 +3087,7 @@ void Session::setWallpapers(const QVector<MTPWallPaper> &data, int32 hash) {
0ULL, // access_hash 0ULL, // access_hash
MTPDwallPaper::Flags(0), MTPDwallPaper::Flags(0),
QString(), // slug QString(), // slug
oldBackground oldBackground.get()
}); });
} }
for (const auto &paper : data) { for (const auto &paper : data) {
@ -3109,7 +3099,7 @@ void Session::setWallpapers(const QVector<MTPWallPaper> &data, int32 hash) {
paper.vaccess_hash.v, paper.vaccess_hash.v,
paper.vflags.v, paper.vflags.v,
qs(paper.vslug), qs(paper.vslug),
document->thumb, document->thumbnail(),
document, document,
}); });
} }

View File

@ -342,15 +342,16 @@ public:
const uint64 &access, const uint64 &access,
const QByteArray &fileReference, const QByteArray &fileReference,
TimeId date, TimeId date,
const ImagePtr &thumb, const ImagePtr &thumbnailInline,
const ImagePtr &medium, const ImagePtr &thumbnailSmall,
const ImagePtr &full); const ImagePtr &thumbnail,
const ImagePtr &large);
void photoConvert( void photoConvert(
not_null<PhotoData*> original, not_null<PhotoData*> original,
const MTPPhoto &data); const MTPPhoto &data);
[[nodiscard]] PhotoData *photoFromWeb( [[nodiscard]] PhotoData *photoFromWeb(
const MTPWebDocument &data, const MTPWebDocument &data,
ImagePtr thumb = ImagePtr(), ImagePtr thumbnailSmall = ImagePtr(),
bool willBecomeNormal = false); bool willBecomeNormal = false);
[[nodiscard]] not_null<DocumentData*> document(DocumentId id); [[nodiscard]] not_null<DocumentData*> document(DocumentId id);
@ -366,7 +367,8 @@ public:
TimeId date, TimeId date,
const QVector<MTPDocumentAttribute> &attributes, const QVector<MTPDocumentAttribute> &attributes,
const QString &mime, const QString &mime,
const ImagePtr &thumb, const ImagePtr &thumbnailInline,
const ImagePtr &thumbnail,
int32 dc, int32 dc,
int32 size, int32 size,
const StorageImageLocation &thumbLocation); const StorageImageLocation &thumbLocation);
@ -571,9 +573,10 @@ private:
const uint64 &access, const uint64 &access,
const QByteArray &fileReference, const QByteArray &fileReference,
TimeId date, TimeId date,
const ImagePtr &thumb, const ImagePtr &thumbnailInline,
const ImagePtr &medium, const ImagePtr &thumbnailSmall,
const ImagePtr &full); const ImagePtr &thumbnail,
const ImagePtr &large);
void documentApplyFields( void documentApplyFields(
not_null<DocumentData*> document, not_null<DocumentData*> document,
@ -588,7 +591,8 @@ private:
TimeId date, TimeId date,
const QVector<MTPDocumentAttribute> &attributes, const QVector<MTPDocumentAttribute> &attributes,
const QString &mime, const QString &mime,
const ImagePtr &thumb, const ImagePtr &thumbnailInline,
const ImagePtr &thumbnail,
int32 dc, int32 dc,
int32 size, int32 size,
const StorageImageLocation &thumbLocation); const StorageImageLocation &thumbLocation);

View File

@ -8,6 +8,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "data/data_types.h" #include "data/data_types.h"
#include "data/data_document.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 "ui/widgets/input_fields.h"
#include "storage/cache/storage_cache_types.h" #include "storage/cache/storage_cache_types.h"
#include "base/openssl_help.h" #include "base/openssl_help.h"
@ -30,6 +32,20 @@ constexpr auto kGeoPointCacheMask = 0x000000FFFFFFFFFFULL;
} // namespace } // 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) { Storage::Cache::Key DocumentCacheKey(int32 dcId, uint64 id) {
return Storage::Cache::Key{ return Storage::Cache::Key{
Data::kDocumentCacheTag | (uint64(dcId) & Data::kDocumentCacheMask), 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 } // namespace Data
void AudioMsgId::setTypeFromAudio() { void AudioMsgId::setTypeFromAudio() {

View File

@ -23,6 +23,11 @@ namespace Ui {
class InputField; class InputField;
} // namespace Ui } // namespace Ui
namespace Images {
enum class Option;
using Options = base::flags<Option>;
} // namespace Images
class StorageImageLocation; class StorageImageLocation;
class WebFileLocation; class WebFileLocation;
struct GeoPointLocation; struct GeoPointLocation;
@ -50,6 +55,35 @@ constexpr auto kVoiceMessageCacheTag = uint8(0x03);
constexpr auto kVideoMessageCacheTag = uint8(0x04); constexpr auto kVideoMessageCacheTag = uint8(0x04);
constexpr auto kAnimationCacheTag = uint8(0x05); 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 } // namespace Data
struct MessageGroupId { struct MessageGroupId {
@ -273,7 +307,7 @@ using PollId = uint64;
using WallPaperId = uint64; using WallPaperId = uint64;
constexpr auto CancelledWebPageId = WebPageId(0xFFFFFFFFFFFFFFFFULL); 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 // [0] == -1 -- counting, [0] == -2 -- could not count
using VoiceWaveform = QVector<signed char>; using VoiceWaveform = QVector<signed char>;

View File

@ -63,7 +63,7 @@ WebPageCollage ExtractCollage(
for (const auto &item : items) { for (const auto &item : items) {
const auto good = item.match([&](const MTPDpageBlockPhoto &data) { const auto good = item.match([&](const MTPDpageBlockPhoto &data) {
const auto photo = storage.photo(data.vphoto_id.v); const auto photo = storage.photo(data.vphoto_id.v);
if (photo->full->isNull()) { if (photo->isNull()) {
return false; return false;
} }
result.items.push_back(photo); result.items.push_back(photo);
@ -231,12 +231,12 @@ void WebPageData::replaceDocumentGoodThumbnail() {
if (!document || !photo || !document->goodThumbnail()) { if (!document || !photo || !document->goodThumbnail()) {
return; return;
} }
const auto &location = photo->full->location(); const auto &location = photo->large()->location();
if (!location.isNull()) { if (!location.isNull()) {
document->replaceGoodThumbnail( document->replaceGoodThumbnail(
std::make_unique<Images::StorageSource>( std::make_unique<Images::StorageSource>(
location, location,
photo->full->bytesSize())); photo->large()->bytesSize()));
} }
} }

View File

@ -1074,7 +1074,7 @@ void InnerWidget::showContextMenu(QContextMenuEvent *e, bool showFromTouch) {
} }
void InnerWidget::savePhotoToFile(PhotoData *photo) { void InnerWidget::savePhotoToFile(PhotoData *photo) {
if (!photo || !photo->date || !photo->loaded()) { if (!photo || photo->isNull() || !photo->loaded()) {
return; return;
} }
@ -1086,7 +1086,7 @@ void InnerWidget::savePhotoToFile(PhotoData *photo) {
filedialogDefaultName(qsl("photo"), qsl(".jpg")), filedialogDefaultName(qsl("photo"), qsl(".jpg")),
crl::guard(this, [=](const QString &result) { crl::guard(this, [=](const QString &result) {
if (!result.isEmpty()) { 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) { 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() { void InnerWidget::copySelectedText() {

View File

@ -1771,7 +1771,7 @@ void HistoryInner::copySelectedText() {
} }
void HistoryInner::savePhotoToFile(not_null<PhotoData*> photo) { 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(); auto filter = qsl("JPEG Image (*.jpg);;") + FileDialog::AllFilesFilter();
FileDialog::GetWritePath( FileDialog::GetWritePath(
@ -1783,15 +1783,15 @@ void HistoryInner::savePhotoToFile(not_null<PhotoData*> photo) {
qsl(".jpg")), qsl(".jpg")),
crl::guard(this, [=](const QString &result) { crl::guard(this, [=](const QString &result) {
if (!result.isEmpty()) { 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) { 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) { void HistoryInner::showStickerPackInfo(not_null<DocumentData*> document) {

View File

@ -6361,7 +6361,7 @@ void HistoryWidget::drawField(Painter &p, const QRect &rect) {
if (drawWebPagePreview) { if (drawWebPagePreview) {
auto previewLeft = st::historyReplySkip + st::webPageLeft; auto previewLeft = st::historyReplySkip + st::webPageLeft;
p.fillRect(st::historyReplySkip, backy + st::msgReplyPadding.top(), st::webPageBar, st::msgReplyBarSize.height(), st::msgInReplyBarColor); 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 const auto preview = _previewData->photo
? _previewData->photo->getReplyPreview(Data::FileOrigin()) ? _previewData->photo->getReplyPreview(Data::FileOrigin())
: _previewData->document->getReplyPreview(Data::FileOrigin()); : _previewData->document->getReplyPreview(Data::FileOrigin());

View File

@ -69,12 +69,13 @@ void HistoryDocument::createComponents(bool caption) {
mask |= HistoryDocumentVoice::Bit(); mask |= HistoryDocumentVoice::Bit();
} else { } else {
mask |= HistoryDocumentNamed::Bit(); mask |= HistoryDocumentNamed::Bit();
if (!_data->isSong() if (const auto thumb = _data->thumbnail()) {
&& !_data->thumb->isNull() if (!_data->isSong()
&& _data->thumb->width() && thumb->width()
&& _data->thumb->height() && thumb->height()
&& !Data::IsExecutableName(_data->filename())) { && !Data::IsExecutableName(_data->filename())) {
mask |= HistoryDocumentThumbed::Bit(); mask |= HistoryDocumentThumbed::Bit();
}
} }
} }
if (caption) { if (caption) {
@ -117,9 +118,9 @@ QSize HistoryDocument::countOptimalSize() {
} }
auto thumbed = Get<HistoryDocumentThumbed>(); auto thumbed = Get<HistoryDocumentThumbed>();
if (thumbed) { if (thumbed) {
_data->thumb->load(_realParent->fullId()); _data->loadThumbnail(_realParent->fullId());
auto tw = ConvertScale(_data->thumb->width()); auto tw = ConvertScale(_data->thumbnail()->width());
auto th = ConvertScale(_data->thumb->height()); auto th = ConvertScale(_data->thumbnail()->height());
if (tw > th) { if (tw > th) {
thumbed->_thumbw = (tw * st::msgFileThumbSize) / th; thumbed->_thumbw = (tw * st::msgFileThumbSize) / th;
} else { } else {
@ -232,10 +233,12 @@ void HistoryDocument::draw(Painter &p, const QRect &r, TextSelection selection,
auto roundRadius = inWebPage ? ImageRoundRadius::Small : ImageRoundRadius::Large; auto roundRadius = inWebPage ? ImageRoundRadius::Small : ImageRoundRadius::Large;
QRect rthumb(rtlrect(st::msgFileThumbPadding.left(), st::msgFileThumbPadding.top() - topMinus, st::msgFileThumbSize, st::msgFileThumbSize, width())); QRect rthumb(rtlrect(st::msgFileThumbPadding.left(), st::msgFileThumbPadding.top() - topMinus, st::msgFileThumbSize, st::msgFileThumbSize, width()));
QPixmap thumb; QPixmap thumb;
if (loaded) { if (const auto normal = _data->thumbnail()) {
thumb = _data->thumb->pixSingle(_realParent->fullId(), thumbed->_thumbw, 0, st::msgFileThumbSize, st::msgFileThumbSize, roundRadius); if (normal->loaded()) {
} else { thumb = normal->pixSingle(_realParent->fullId(), thumbed->_thumbw, 0, st::msgFileThumbSize, st::msgFileThumbSize, roundRadius);
thumb = _data->thumb->pixBlurredSingle(_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); p.drawPixmap(rthumb.topLeft(), thumb);
if (selected) { if (selected) {
@ -528,7 +531,7 @@ TextState HistoryDocument::textState(QPoint point, StateRequest request) const {
painth -= st::msgPadding.bottom(); 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; result.link = _openl;
return result; return result;
} }

View File

@ -56,7 +56,7 @@ HistoryGif::HistoryGif(
setStatusSize(FileStatusSizeReady); setStatusSize(FileStatusSizeReady);
_caption = createCaption(item); _caption = createCaption(item);
_data->thumb->load(item->fullId()); _data->loadThumbnail(item->fullId());
} }
QSize HistoryGif::countOptimalSize() { QSize HistoryGif::countOptimalSize() {
@ -89,8 +89,8 @@ QSize HistoryGif::countOptimalSize() {
} else { } else {
tw = ConvertScale(_data->dimensions.width()), th = ConvertScale(_data->dimensions.height()); tw = ConvertScale(_data->dimensions.width()), th = ConvertScale(_data->dimensions.height());
if (!tw || !th) { if (!tw || !th) {
tw = ConvertScale(_data->thumb->width()); tw = ConvertScale(_data->thumbnail()->width());
th = ConvertScale(_data->thumb->height()); th = ConvertScale(_data->thumbnail()->height());
} }
} }
const auto maxSize = _data->isVideoMessage() const auto maxSize = _data->isVideoMessage()
@ -147,8 +147,8 @@ QSize HistoryGif::countCurrentSize(int newWidth) {
} else { } else {
tw = ConvertScale(_data->dimensions.width()), th = ConvertScale(_data->dimensions.height()); tw = ConvertScale(_data->dimensions.width()), th = ConvertScale(_data->dimensions.height());
if (!tw || !th) { if (!tw || !th) {
tw = ConvertScale(_data->thumb->width()); tw = ConvertScale(_data->thumbnail()->width());
th = ConvertScale(_data->thumb->height()); th = ConvertScale(_data->thumbnail()->height());
} }
} }
const auto maxSize = _data->isVideoMessage() const auto maxSize = _data->isVideoMessage()
@ -347,7 +347,13 @@ void HistoryGif::draw(Painter &p, const QRect &r, TextSelection selection, TimeM
if (good) { if (good) {
good->load({}); 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));
}
}
} }
} }

View File

@ -59,7 +59,11 @@ void HistoryPhoto::create(FullMsgId contextId, PeerData *chat) {
std::make_shared<PhotoOpenClickHandler>(_data, contextId, chat), std::make_shared<PhotoOpenClickHandler>(_data, contextId, chat),
std::make_shared<PhotoSaveClickHandler>(_data, contextId, chat), std::make_shared<PhotoSaveClickHandler>(_data, contextId, chat),
std::make_shared<PhotoCancelClickHandler>(_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() { QSize HistoryPhoto::countOptimalSize() {
@ -74,8 +78,8 @@ QSize HistoryPhoto::countOptimalSize() {
auto maxWidth = 0; auto maxWidth = 0;
auto minHeight = 0; auto minHeight = 0;
auto tw = ConvertScale(_data->full->width()); auto tw = ConvertScale(_data->width());
auto th = ConvertScale(_data->full->height()); auto th = ConvertScale(_data->height());
if (!tw || !th) { if (!tw || !th) {
tw = th = 1; tw = th = 1;
} }
@ -106,7 +110,7 @@ QSize HistoryPhoto::countOptimalSize() {
} }
QSize HistoryPhoto::countCurrentSize(int newWidth) { 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) { if (tw > st::maxMediaSize) {
th = (st::maxMediaSize * th) / tw; th = (st::maxMediaSize * th) / tw;
tw = st::maxMediaSize; 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()); auto rthumb = rtlrect(paintx, painty, paintw, painth, width());
if (_serviceWidth > 0) { if (_serviceWidth > 0) {
const auto pix = loaded const auto pix = [&] {
? _data->full->pixCircled(_realParent->fullId(), _pixw, _pixh) if (loaded) {
: _data->thumb->pixBlurredCircled(_realParent->fullId(), _pixw, _pixh); 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); p.drawPixmap(rthumb.topLeft(), pix);
} else { } else {
if (bubble) { 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 roundRadius = inWebPage ? ImageRoundRadius::Small : ImageRoundRadius::Large;
auto roundCorners = inWebPage ? RectPart::AllCorners : ((isBubbleTop() ? (RectPart::TopLeft | RectPart::TopRight) : RectPart::None) auto roundCorners = inWebPage ? RectPart::AllCorners : ((isBubbleTop() ? (RectPart::TopLeft | RectPart::TopRight) : RectPart::None)
| ((isBubbleBottom() && _caption.isEmpty()) ? (RectPart::BottomLeft | RectPart::BottomRight) : RectPart::None)); | ((isBubbleBottom() && _caption.isEmpty()) ? (RectPart::BottomLeft | RectPart::BottomRight) : RectPart::None));
const auto pix = loaded const auto pix = [&] {
? _data->full->pixSingle(_realParent->fullId(), _pixw, _pixh, paintw, painth, roundRadius, roundCorners) if (loaded) {
: _data->thumb->pixBlurredSingle(_realParent->fullId(), _pixw, _pixh, paintw, painth, roundRadius, roundCorners); 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); p.drawPixmap(rthumb.topLeft(), pix);
if (selected) { if (selected) {
App::complexOverlayRect(p, rthumb, roundRadius, roundCorners); 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* { auto icon = ([radial, this, selected]() -> const style::icon* {
if (radial || _data->loading()) { if (radial || _data->loading()) {
if (_data->uploading() if (_data->uploading()
|| !_data->full->location().isNull()) { || !_data->large()->location().isNull()) {
return &(selected ? st::historyFileThumbCancelSelected : st::historyFileThumbCancel); return &(selected ? st::historyFileThumbCancelSelected : st::historyFileThumbCancel);
} }
return nullptr; return nullptr;
@ -292,7 +316,7 @@ TextState HistoryPhoto::textState(QPoint point, StateRequest request) const {
} else if (_data->loaded()) { } else if (_data->loaded()) {
result.link = _openl; result.link = _openl;
} else if (_data->loading()) { } else if (_data->loading()) {
if (!_data->full->location().isNull()) { if (!_data->large()->location().isNull()) {
result.link = _cancell; result.link = _cancell;
} }
} else { } else {
@ -317,8 +341,8 @@ TextState HistoryPhoto::textState(QPoint point, StateRequest request) const {
} }
QSize HistoryPhoto::sizeForGrouping() const { QSize HistoryPhoto::sizeForGrouping() const {
const auto width = _data->full->width(); const auto width = _data->width();
const auto height = _data->full->height(); const auto height = _data->height();
return { std::max(width, 1), std::max(height, 1) }; return { std::max(width, 1), std::max(height, 1) };
} }
@ -395,7 +419,7 @@ void HistoryPhoto::drawGrouped(
if (_data->waitingForAlbum()) { if (_data->waitingForAlbum()) {
return &(selected ? st::historyFileThumbWaitingSelected : st::historyFileThumbWaiting); return &(selected ? st::historyFileThumbWaitingSelected : st::historyFileThumbWaiting);
} else if (radial || _data->loading()) { } 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 &(selected ? st::historyFileThumbCancelSelected : st::historyFileThumbCancel);
} }
return nullptr; return nullptr;
@ -440,7 +464,7 @@ TextState HistoryPhoto::getStateGrouped(
: _data->loaded() : _data->loaded()
? _openl ? _openl
: _data->loading() : _data->loading()
? (_data->full->location().isNull() ? (_data->large()->location().isNull()
? ClickHandlerPtr() ? ClickHandlerPtr()
: _cancell) : _cancell)
: _savel); : _savel);
@ -470,7 +494,13 @@ void HistoryPhoto::validateGroupedCache(
not_null<QPixmap*> cache) const { not_null<QPixmap*> cache) const {
using Option = Images::Option; using Option = Images::Option;
const auto loaded = _data->loaded(); 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 width = geometry.width();
const auto height = geometry.height(); const auto height = geometry.height();
const auto options = Option::Smooth const auto options = Option::Smooth
@ -488,14 +518,22 @@ void HistoryPhoto::validateGroupedCache(
return; return;
} }
const auto originalWidth = ConvertScale(_data->full->width()); const auto originalWidth = ConvertScale(_data->width());
const auto originalHeight = ConvertScale(_data->full->height()); const auto originalHeight = ConvertScale(_data->height());
const auto pixSize = Ui::GetImageScaleSizeForGeometry( const auto pixSize = Ui::GetImageScaleSizeForGeometry(
{ originalWidth, originalHeight }, { originalWidth, originalHeight },
{ width, height }); { width, height });
const auto pixWidth = pixSize.width() * cIntRetinaFactor(); const auto pixWidth = pixSize.width() * cIntRetinaFactor();
const auto pixHeight = pixSize.height() * 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; *cacheKey = key;
*cache = image->pixNoCache(_realParent->fullId(), pixWidth, pixHeight, options, width, height); *cache = image->pixNoCache(_realParent->fullId(), pixWidth, pixHeight, options, width, height);

View File

@ -30,8 +30,8 @@ HistorySticker::HistorySticker(
: HistoryMedia(parent) : HistoryMedia(parent)
, _data(document) , _data(document)
, _emoji(_data->sticker()->alt) { , _emoji(_data->sticker()->alt) {
_data->thumb->load(parent->data()->fullId()); _data->loadThumbnail(parent->data()->fullId());
if (auto emoji = Ui::Emoji::Find(_emoji)) { if (const auto emoji = Ui::Emoji::Find(_emoji)) {
_emoji = emoji->text(); _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; if (width() < st::msgPadding.left() + st::msgPadding.right() + 1) return;
_data->checkSticker(); _data->checkStickerLarge();
bool loaded = _data->loaded(); bool loaded = _data->loaded();
bool selected = (selection == FullSelection); 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 w = _pixw;
const auto h = _pixh; const auto h = _pixh;
const auto &c = st::msgStickerOverlay; const auto &c = st::msgStickerOverlay;
if (const auto image = _data->getStickerImage()) { if (const auto image = _data->getStickerLarge()) {
return selected return selected
? image->pixColored(o, c, w, h) ? image->pixColored(o, c, w, h)
: image->pix(o, 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( p.drawPixmap(
QPoint{ usex + (usew - _pixw) / 2, (minHeight() - _pixh) / 2 }, QPoint{ usex + (usew - _pixw) / 2, (minHeight() - _pixh) / 2 },

View File

@ -41,7 +41,21 @@ HistoryVideo::HistoryVideo(
setStatusSize(FileStatusSizeReady); 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() { QSize HistoryVideo::countOptimalSize() {
@ -53,8 +67,9 @@ QSize HistoryVideo::countOptimalSize() {
_parent->skipBlockHeight()); _parent->skipBlockHeight());
} }
auto tw = ConvertScale(_data->thumb->width()); const auto size = sizeForAspectRatio();
auto th = ConvertScale(_data->thumb->height()); auto tw = ConvertScale(size.width());
auto th = ConvertScale(size.height());
if (!tw || !th) { if (!tw || !th) {
tw = th = 1; tw = th = 1;
} }
@ -86,7 +101,9 @@ QSize HistoryVideo::countOptimalSize() {
} }
QSize HistoryVideo::countCurrentSize(int newWidth) { 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) { if (!tw || !th) {
tw = th = 1; tw = th = 1;
} }
@ -166,7 +183,12 @@ void HistoryVideo::draw(Painter &p, const QRect &r, TextSelection selection, Tim
if (good) { if (good) {
good->load({}); 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) { if (selected) {
App::complexOverlayRect(p, rthumb, roundRadius, roundCorners); App::complexOverlayRect(p, rthumb, roundRadius, roundCorners);
@ -288,13 +310,7 @@ TextState HistoryVideo::textState(QPoint point, StateRequest request) const {
} }
QSize HistoryVideo::sizeForGrouping() const { QSize HistoryVideo::sizeForGrouping() const {
const auto width = _data->dimensions.isEmpty() return sizeForAspectRatio();
? _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) };
} }
void HistoryVideo::drawGrouped( void HistoryVideo::drawGrouped(
@ -384,7 +400,6 @@ void HistoryVideo::drawGrouped(
p.setOpacity(backOpacity); p.setOpacity(backOpacity);
if (icon) { if (icon) {
if (previous && radialOpacity > 0. && radialOpacity < 1.) { if (previous && radialOpacity > 0. && radialOpacity < 1.) {
LOG(("INTERPOLATING: %1").arg(radialOpacity));
PaintInterpolatedIcon(p, *icon, *previous, radialOpacity, inner); PaintInterpolatedIcon(p, *icon, *previous, radialOpacity, inner);
} else { } else {
icon->paintInCenter(p, inner); icon->paintInCenter(p, inner);
@ -442,13 +457,18 @@ void HistoryVideo::validateGroupedCache(
using Option = Images::Option; using Option = Images::Option;
const auto good = _data->goodThumbnail(); const auto good = _data->goodThumbnail();
const auto useGood = (good && good->loaded()); 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) { if (good && !useGood) {
good->load({}); good->load({});
} }
const auto loaded = useGood ? true : _data->thumb->loaded(); const auto loadLevel = useGood ? 3 : useThumb ? 2 : image ? 1 : 0;
const auto loadLevel = loaded ? 1 : 0;
const auto width = geometry.width(); const auto width = geometry.width();
const auto height = geometry.height(); const auto height = geometry.height();
const auto options = Option::Smooth const auto options = Option::Smooth
@ -466,8 +486,9 @@ void HistoryVideo::validateGroupedCache(
return; return;
} }
const auto originalWidth = ConvertScale(_data->thumb->width()); const auto original = sizeForAspectRatio();
const auto originalHeight = ConvertScale(_data->thumb->height()); const auto originalWidth = ConvertScale(original.width());
const auto originalHeight = ConvertScale(original.height());
const auto pixSize = Ui::GetImageScaleSizeForGeometry( const auto pixSize = Ui::GetImageScaleSizeForGeometry(
{ originalWidth, originalHeight }, { originalWidth, originalHeight },
{ width, height }); { width, height });
@ -475,7 +496,7 @@ void HistoryVideo::validateGroupedCache(
const auto pixHeight = pixSize.height() * cIntRetinaFactor(); const auto pixHeight = pixSize.height() * cIntRetinaFactor();
*cacheKey = key; *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 { void HistoryVideo::setStatusSize(int newSize) const {

View File

@ -83,6 +83,7 @@ private:
not_null<QPixmap*> cache) const; not_null<QPixmap*> cache) const;
void setStatusSize(int newSize) const; void setStatusSize(int newSize) const;
void updateStatusText() const; void updateStatusText() const;
QSize sizeForAspectRatio() const;
not_null<DocumentData*> _data; not_null<DocumentData*> _data;
int _thumbw = 1; int _thumbw = 1;

View File

@ -34,14 +34,16 @@ namespace {
constexpr auto kMaxOriginalEntryLines = 8192; constexpr auto kMaxOriginalEntryLines = 8192;
int articleThumbWidth(PhotoData *thumb, int height) { int articleThumbWidth(not_null<PhotoData*> thumb, int height) {
auto w = thumb->medium->width(); auto w = thumb->thumbnail()->width();
auto h = thumb->medium->height(); auto h = thumb->thumbnail()->height();
return qMax(qMin(height * w / h, height), 1); return qMax(qMin(height * w / h, height), 1);
} }
int articleThumbHeight(PhotoData *thumb, int width) { int articleThumbHeight(not_null<PhotoData*> thumb, int width) {
return qMax(thumb->medium->height() * width / thumb->medium->width(), 1); return qMax(
thumb->thumbnail()->height() * width / thumb->thumbnail()->width(),
1);
} }
std::vector<std::unique_ptr<Data::Media>> PrepareCollageMedia( 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(); auto lineHeight = unitedLineHeight();
if (_asArticle) { if (_asArticle) {
const auto contextId = _parent->data()->fullId(); const auto contextId = _parent->data()->fullId();
_data->photo->medium->load(contextId, false, false); _data->photo->loadThumbnail(contextId);
bool full = _data->photo->medium->loaded(); bool full = _data->photo->thumbnail()->loaded();
QPixmap pix; QPixmap pix;
auto pw = qMax(_pixw, lineHeight); auto pw = qMax(_pixw, lineHeight);
auto ph = _pixh; auto ph = _pixh;
auto pixw = _pixw, pixh = articleThumbHeight(_data->photo, _pixw); 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) { 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)); float64 coef = (pixw * ph > pixh * pw) ? qMin(ph / float64(pixh), maxh / float64(pixh)) : qMin(pw / float64(pixw), maxw / float64(pixw));
pixh = qRound(pixh * coef); pixh = qRound(pixh * coef);
pixw = qRound(pixw * coef); pixw = qRound(pixw * coef);
} }
if (full) { if (full) {
pix = _data->photo->medium->pixSingle(contextId, pixw, pixh, pw, ph, ImageRoundRadius::Small); pix = _data->photo->thumbnail()->pixSingle(contextId, pixw, pixh, pw, ph, ImageRoundRadius::Small);
} else { } else if (_data->photo->thumbnailSmall()->loaded()) {
pix = _data->photo->thumb->pixBlurredSingle(contextId, pixw, pixh, pw, ph, ImageRoundRadius::Small); 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); p.drawPixmapLeft(padding.left() + paintw - pw, tshift, width(), pix);
if (selected) { if (selected) {

View File

@ -49,7 +49,7 @@ void AddToggleGroupingAction(
} }
void SavePhotoToFile(not_null<PhotoData*> photo) { void SavePhotoToFile(not_null<PhotoData*> photo) {
if (!photo->date || !photo->loaded()) { if (photo->isNull() || !photo->loaded()) {
return; return;
} }
@ -60,17 +60,17 @@ void SavePhotoToFile(not_null<PhotoData*> photo) {
filedialogDefaultName(qsl("photo"), qsl(".jpg")), filedialogDefaultName(qsl("photo"), qsl(".jpg")),
crl::guard(&Auth(), [=](const QString &result) { crl::guard(&Auth(), [=](const QString &result) {
if (!result.isEmpty()) { if (!result.isEmpty()) {
photo->full->pix(Data::FileOrigin()).toImage().save(result, "JPG"); photo->large()->original().save(result, "JPG");
} }
})); }));
} }
void CopyImage(not_null<PhotoData*> photo) { void CopyImage(not_null<PhotoData*> photo) {
if (!photo->date || !photo->loaded()) { if (photo->isNull() || !photo->loaded()) {
return; return;
} }
QApplication::clipboard()->setPixmap(photo->full->pix(Data::FileOrigin())); QApplication::clipboard()->setImage(photo->large()->original());
} }
void ShowStickerPackInfo(not_null<DocumentData*> document) { void ShowStickerPackInfo(not_null<DocumentData*> document) {

View File

@ -48,23 +48,25 @@ DocumentData *FileBase::getShownDocument() const {
} }
int FileBase::content_width() const { int FileBase::content_width() const {
DocumentData *document = getShownDocument(); if (const auto document = getShownDocument()) {
if (document->dimensions.width() > 0) { if (document->dimensions.width() > 0) {
return document->dimensions.width(); return document->dimensions.width();
} }
if (!document->thumb->isNull()) { if (const auto thumb = document->thumbnail()) {
return ConvertScale(document->thumb->width()); return ConvertScale(thumb->width());
}
} }
return 0; return 0;
} }
int FileBase::content_height() const { int FileBase::content_height() const {
DocumentData *document = getShownDocument(); if (const auto document = getShownDocument()) {
if (document->dimensions.height() > 0) { if (document->dimensions.height() > 0) {
return document->dimensions.height(); return document->dimensions.height();
} }
if (!document->thumb->isNull()) { if (const auto thumb = document->thumbnail()) {
return ConvertScale(document->thumb->height()); return ConvertScale(thumb->height());
}
} }
return 0; return 0;
} }
@ -82,10 +84,10 @@ int FileBase::content_duration() const {
return getResultDuration(); return getResultDuration();
} }
ImagePtr FileBase::content_thumb() const { Image *FileBase::content_thumb() const {
if (DocumentData *document = getShownDocument()) { if (const auto document = getShownDocument()) {
if (!document->thumb->isNull()) { if (const auto thumb = document->thumbnail()) {
return document->thumb; return thumb;
} }
} }
return getResultThumb(); 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); auto pixmap = _gif->current(frame.width(), frame.height(), _width, height, ImageRoundRadius::None, RectPart::None, context->paused ? 0 : context->ms);
p.drawPixmap(r.topLeft(), pixmap); p.drawPixmap(r.topLeft(), pixmap);
} else { } else {
prepareThumb(_width, height, frame); prepareThumbnail({ _width, height }, frame);
if (_thumb.isNull()) { if (_thumb.isNull()) {
p.fillRect(r, st::overviewPhotoBg); p.fillRect(r, st::overviewPhotoBg);
} else { } else {
@ -287,29 +289,37 @@ QSize Gif::countFrameSize() const {
return QSize(framew, frameh); return QSize(framew, frameh);
} }
void Gif::prepareThumb(int32 width, int32 height, const QSize &frame) const { void Gif::validateThumbnail(
const auto origin = fileOrigin(); 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 (const auto document = getShownDocument()) {
if (!document->thumb->isNull()) { validateThumbnail(document->thumbnail(), size, frame, true);
if (document->thumb->loaded()) { validateThumbnail(document->thumbnailInline(), size, frame, false);
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);
}
}
} else { } else {
const auto thumb = getResultThumb(); validateThumbnail(getResultThumb(), size, frame, true);
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);
}
}
} }
} }
@ -386,7 +396,7 @@ void Sticker::initDimensions() {
void Sticker::preload() const { void Sticker::preload() const {
if (const auto document = getShownDocument()) { if (const auto document = getShownDocument()) {
document->checkStickerThumb(); document->checkStickerSmall();
} else if (const auto thumb = getResultThumb()) { } else if (const auto thumb = getResultThumb()) {
thumb->load(fileOrigin()); thumb->load(fileOrigin());
} }
@ -402,7 +412,7 @@ void Sticker::paint(Painter &p, const QRect &clip, const PaintContext *context)
p.setOpacity(1); p.setOpacity(1);
} }
prepareThumb(); prepareThumbnail();
if (!_thumb.isNull()) { if (!_thumb.isNull()) {
int w = _thumb.width() / cIntRetinaFactor(), h = _thumb.height() / cIntRetinaFactor(); int w = _thumb.width() / cIntRetinaFactor(), h = _thumb.height() / cIntRetinaFactor();
QPoint pos = QPoint((st::stickerPanSize.width() - w) / 2, (st::stickerPanSize.height() - h) / 2); 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)); return QSize(qMax(w, 1), qMax(h, 1));
} }
void Sticker::prepareThumb() const { void Sticker::prepareThumbnail() const {
if (const auto document = getShownDocument()) { if (const auto document = getShownDocument()) {
document->checkStickerThumb(); document->checkStickerSmall();
if (const auto sticker = document->getStickerThumb()) { if (const auto sticker = document->getStickerSmall()) {
if (!_thumbLoaded && sticker->loaded()) { if (!_thumbLoaded && sticker->loaded()) {
const auto thumbSize = getThumbSize(); const auto thumbSize = getThumbSize();
_thumb = sticker->pix( _thumb = sticker->pix(
@ -457,18 +467,19 @@ void Sticker::prepareThumb() const {
} }
} else { } else {
const auto origin = fileOrigin(); const auto origin = fileOrigin();
const auto thumb = getResultThumb(); if (const auto thumb = getResultThumb()) {
if (thumb->loaded()) { if (thumb->loaded()) {
if (!_thumbLoaded) { if (!_thumbLoaded) {
const auto thumbSize = getThumbSize(); const auto thumbSize = getThumbSize();
_thumb = thumb->pix( _thumb = thumb->pix(
origin, origin,
thumbSize.width(), thumbSize.width(),
thumbSize.height()); thumbSize.height());
_thumbLoaded = true; _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() { void Photo::initDimensions() {
PhotoData *photo = getShownPhoto(); const auto photo = getShownPhoto();
int32 w = photo->full->width(), h = photo->full->height(); int32 w = photo->width(), h = photo->height();
if (w <= 0 || h <= 0) { if (w <= 0 || h <= 0) {
_maxw = 0; _maxw = 0;
} else { } else {
@ -495,7 +506,7 @@ void Photo::paint(Painter &p, const QRect &clip, const PaintContext *context) co
QRect r(0, 0, _width, height); QRect r(0, 0, _width, height);
prepareThumb(_width, height, frame); prepareThumbnail({ _width, height }, frame);
if (_thumb.isNull()) { if (_thumb.isNull()) {
p.fillRect(r, st::overviewPhotoBg); p.fillRect(r, st::overviewPhotoBg);
} else { } else {
@ -521,7 +532,7 @@ PhotoData *Photo::getShownPhoto() const {
QSize Photo::countFrameSize() const { QSize Photo::countFrameSize() const {
PhotoData *photo = getShownPhoto(); 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 * height > frameh * _width) {
if (framew < st::maxStickerSize || frameh > height) { if (framew < st::maxStickerSize || frameh > height) {
if (frameh > height || (framew * height / frameh) <= st::maxStickerSize) { if (frameh > height || (framew * height / frameh) <= st::maxStickerSize) {
@ -546,31 +557,38 @@ QSize Photo::countFrameSize() const {
return QSize(framew, frameh); 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(); 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 (const auto photo = getShownPhoto()) {
if (photo->medium->loaded()) { validateThumbnail(photo->thumbnail(), size, frame, true);
if (!_thumbLoaded || _thumb.width() != width * cIntRetinaFactor() || _thumb.height() != height * cIntRetinaFactor()) { validateThumbnail(photo->thumbnailSmall(), size, frame, false);
_thumb = photo->medium->pixNoCache(origin, frame.width() * cIntRetinaFactor(), frame.height() * cIntRetinaFactor(), Images::Option::Smooth, width, height); validateThumbnail(photo->thumbnailInline(), size, frame, false);
} } else if (const auto thumbnail = getResultThumb()) {
_thumbLoaded = true; validateThumbnail(thumbnail, size, frame, 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);
}
} }
} }
@ -585,7 +603,7 @@ Video::Video(not_null<Context*> context, Result *result) : FileBase(context, res
} }
void Video::initDimensions() { void Video::initDimensions() {
bool withThumb = !content_thumb()->isNull(); const auto withThumb = (content_thumb() != nullptr);
_maxw = st::emojiPanWidth - st::emojiScroll.width - st::inlineResultsLeft; _maxw = st::emojiPanWidth - st::emojiScroll.width - st::inlineResultsLeft;
int32 textWidth = _maxw - (withThumb ? (st::inlineThumbSize + st::inlineThumbSkip) : 0); 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 { void Video::paint(Painter &p, const QRect &clip, const PaintContext *context) const {
int left = st::inlineThumbSize + st::inlineThumbSkip; int left = st::inlineThumbSize + st::inlineThumbSkip;
bool withThumb = !content_thumb()->isNull(); const auto withThumb = (content_thumb() != nullptr);
if (withThumb) { if (withThumb) {
prepareThumb(st::inlineThumbSize, st::inlineThumbSize); prepareThumbnail({ st::inlineThumbSize, st::inlineThumbSize });
if (_thumb.isNull()) { if (_thumb.isNull()) {
p.fillRect(rtlrect(0, st::inlineRowMargin, st::inlineThumbSize, st::inlineThumbSize, _width), st::overviewPhotoBg); p.fillRect(rtlrect(0, st::inlineRowMargin, st::inlineThumbSize, st::inlineThumbSize, _width), st::overviewPhotoBg);
} else { } else {
@ -661,11 +679,15 @@ TextState Video::getState(
return {}; 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 thumb = content_thumb();
const auto origin = fileOrigin(); const auto origin = fileOrigin();
if (thumb->loaded()) { 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); int32 w = qMax(ConvertScale(thumb->width()), 1), h = qMax(ConvertScale(thumb->height()), 1);
if (w * height > h * width) { if (w * height > h * width) {
if (height < h) { if (height < h) {
@ -948,7 +970,7 @@ void Contact::paint(Painter &p, const QRect &clip, const PaintContext *context)
int32 left = st::emojiPanHeaderLeft - st::inlineResultsLeft; int32 left = st::emojiPanHeaderLeft - st::inlineResultsLeft;
left = st::msgFileSize + st::inlineThumbSkip; 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)); QRect rthumb(rtlrect(0, st::inlineRowMargin, st::msgFileSize, st::msgFileSize, _width));
p.drawPixmapLeft(rthumb.topLeft(), _width, _thumb); p.drawPixmapLeft(rthumb.topLeft(), _width, _thumb);
@ -978,9 +1000,9 @@ TextState Contact::getState(
return {}; return {};
} }
void Contact::prepareThumb(int width, int height) const { void Contact::prepareThumbnail(int width, int height) const {
const auto thumb = getResultThumb(); const auto thumb = getResultThumb();
if (thumb->isNull()) { if (!thumb) {
if (_thumb.width() != width * cIntRetinaFactor() || _thumb.height() != height * cIntRetinaFactor()) { if (_thumb.width() != width * cIntRetinaFactor() || _thumb.height() != height * cIntRetinaFactor()) {
_thumb = getResultContactAvatar(width, height); _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; int32 left = st::emojiPanHeaderLeft - st::inlineResultsLeft;
if (_withThumb) { if (_withThumb) {
left = st::inlineThumbSize + st::inlineThumbSkip; 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)); QRect rthumb(rtlrect(0, st::inlineRowMargin, st::inlineThumbSize, st::inlineThumbSize, _width));
if (_thumb.isNull()) { if (_thumb.isNull()) {
ImagePtr thumb = getResultThumb(); const auto thumb = getResultThumb();
if (thumb->isNull() && !_thumbLetter.isEmpty()) { if (!thumb && !_thumbLetter.isEmpty()) {
int32 index = (_thumbLetter.at(0).unicode() % 4); int32 index = (_thumbLetter.at(0).unicode() % 4);
style::color colors[] = { style::color colors[] = {
st::msgFile3Bg, st::msgFile3Bg,
@ -1126,9 +1148,9 @@ TextState Article::getState(
return {}; return {};
} }
void Article::prepareThumb(int width, int height) const { void Article::prepareThumbnail(int width, int height) const {
const auto thumb = getResultThumb(); const auto thumb = getResultThumb();
if (thumb->isNull()) { if (!thumb) {
if (_thumb.width() != width * cIntRetinaFactor() || _thumb.height() != height * cIntRetinaFactor()) { if (_thumb.width() != width * cIntRetinaFactor() || _thumb.height() != height * cIntRetinaFactor()) {
_thumb = getResultContactAvatar(width, height); _thumb = getResultContactAvatar(width, height);
} }
@ -1258,7 +1280,7 @@ void Game::paint(Painter &p, const QRect &clip, const PaintContext *context) con
} }
if (!thumbDisplayed) { if (!thumbDisplayed) {
prepareThumb(st::inlineThumbSize, st::inlineThumbSize); prepareThumbnail({ st::inlineThumbSize, st::inlineThumbSize });
if (_thumb.isNull()) { if (_thumb.isNull()) {
p.fillRect(rthumb, st::overviewPhotoBg); p.fillRect(rthumb, st::overviewPhotoBg);
} else { } else {
@ -1302,41 +1324,52 @@ TextState Game::getState(
return {}; return {};
} }
void Game::prepareThumb(int width, int height) const { void Game::prepareThumbnail(QSize size) const {
const auto thumb = [this] { if (const auto photo = getResultPhoto()) {
if (auto photo = getResultPhoto()) { validateThumbnail(photo->thumbnail(), size, true);
return photo->medium; validateThumbnail(photo->thumbnailInline(), size, false);
} else if (auto document = getResultDocument()) { } else if (const auto document = getResultDocument()) {
return document->thumb; validateThumbnail(document->thumbnail(), size, true);
} validateThumbnail(document->thumbnailInline(), size, false);
return ImagePtr(); }
}(); }
if (thumb->isNull()) {
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; return;
} }
const auto width = size.width();
const auto origin = fileOrigin(); const auto height = size.height();
if (thumb->loaded()) { auto w = qMax(ConvertScale(image->width()), 1);
if (_thumb.width() != width * cIntRetinaFactor() || _thumb.height() != height * cIntRetinaFactor()) { auto h = qMax(ConvertScale(image->height()), 1);
int w = qMax(ConvertScale(thumb->width()), 1), h = qMax(ConvertScale(thumb->height()), 1); auto resizeByHeight1 = (w * height > h * width) && (h >= height);
auto resizeByHeight1 = (w * height > h * width) && (h >= height); auto resizeByHeight2 = (h * width >= w * height) && (w < width);
auto resizeByHeight2 = (h * width >= w * height) && (w < width); if (resizeByHeight1 || resizeByHeight2) {
if (resizeByHeight1 || resizeByHeight2) { if (h > height) {
if (h > height) { w = w * height / h;
w = w * height / h; h = height;
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);
} }
} else { } 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 { bool Game::isRadialAnimation(TimeMs ms) const {

View File

@ -28,7 +28,7 @@ protected:
int content_width() const; int content_width() const;
int content_height() const; int content_height() const;
int content_duration() const; int content_duration() const;
ImagePtr content_thumb() const; Image *content_thumb() const;
}; };
class DeleteSavedGifClickHandler : public LeftButtonClickHandler { class DeleteSavedGifClickHandler : public LeftButtonClickHandler {
@ -83,7 +83,13 @@ private:
Media::Clip::ReaderPointer _gif; Media::Clip::ReaderPointer _gif;
ClickHandlerPtr _delete; ClickHandlerPtr _delete;
mutable QPixmap _thumb; 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; void ensureAnimation() const;
bool isRadialAnimation(TimeMs ms) const; bool isRadialAnimation(TimeMs ms) const;
@ -131,8 +137,13 @@ private:
QSize countFrameSize() const; QSize countFrameSize() const;
mutable QPixmap _thumb; mutable QPixmap _thumb;
mutable bool _thumbLoaded = false; mutable bool _thumbGood = false;
void prepareThumb(int32 width, int32 height, const QSize &frame) const; 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 QPixmap _thumb;
mutable bool _thumbLoaded = false; mutable bool _thumbLoaded = false;
void prepareThumb() const; void prepareThumbnail() const;
}; };
@ -191,7 +202,7 @@ private:
QString _duration; QString _duration;
int _durationWidth = 0; int _durationWidth = 0;
void prepareThumb(int32 width, int32 height) const; void prepareThumbnail(QSize size) const;
}; };
@ -302,7 +313,7 @@ private:
mutable QPixmap _thumb; mutable QPixmap _thumb;
Text _title, _description; 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; QString _thumbLetter, _urlText;
int32 _urlWidth; int32 _urlWidth;
void prepareThumb(int width, int height) const; void prepareThumbnail(int width, int height) const;
}; };
@ -346,7 +357,8 @@ public:
private: private:
void countFrameSize(); 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; bool isRadialAnimation(TimeMs ms) const;
void step_radial(TimeMs ms, bool timer); void step_radial(TimeMs ms, bool timer);
@ -355,6 +367,7 @@ private:
Media::Clip::ReaderPointer _gif; Media::Clip::ReaderPointer _gif;
mutable QPixmap _thumb; mutable QPixmap _thumb;
mutable bool _thumbGood = false;
mutable std::unique_ptr<Ui::RadialAnimation> _radial; mutable std::unique_ptr<Ui::RadialAnimation> _radial;
Text _title, _description; Text _title, _description;

View File

@ -78,16 +78,16 @@ void ItemBase::preload() const {
const auto origin = fileOrigin(); const auto origin = fileOrigin();
if (_result) { if (_result) {
if (_result->_photo) { if (_result->_photo) {
_result->_photo->thumb->load(origin); _result->_photo->loadThumbnail(origin);
} else if (_result->_document) { } else if (_result->_document) {
_result->_document->thumb->load(origin); _result->_document->loadThumbnail(origin);
} else if (!_result->_thumb->isNull()) { } else if (!_result->_thumb->isNull()) {
_result->_thumb->load(origin); _result->_thumb->load(origin);
} }
} else if (_doc) { } else if (_doc) {
_doc->thumb->load(origin); _doc->loadThumbnail(origin);
} else if (_photo) { } else if (_photo) {
_photo->medium->load(origin); _photo->loadThumbnail(origin);
} }
} }
@ -145,17 +145,17 @@ PhotoData *ItemBase::getResultPhoto() const {
return _result ? _result->_photo : nullptr; return _result ? _result->_photo : nullptr;
} }
ImagePtr ItemBase::getResultThumb() const { Image *ItemBase::getResultThumb() const {
if (_result) { if (_result) {
if (_result->_photo && !_result->_photo->thumb->isNull()) { if (_result->_photo) {
return _result->_photo->thumb; 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 { QPixmap ItemBase::getResultContactAvatar(int width, int height) const {

View File

@ -95,7 +95,7 @@ public:
protected: protected:
DocumentData *getResultDocument() const; DocumentData *getResultDocument() const;
PhotoData *getResultPhoto() const; PhotoData *getResultPhoto() const;
ImagePtr getResultThumb() const; Image *getResultThumb() const;
QPixmap getResultContactAvatar(int width, int height) const; QPixmap getResultContactAvatar(int width, int height) const;
int getResultDuration() const; int getResultDuration() const;
QString getResultUrl() const; QString getResultUrl() const;

View File

@ -110,8 +110,8 @@ std::unique_ptr<Result> Result::create(uint64 queryId, const MTPBotInlineResult
message = &r.vsend_message; message = &r.vsend_message;
} break; } break;
} }
auto badAttachment = (result->_photo && result->_photo->full->isNull()) auto badAttachment = (result->_photo && result->_photo->isNull())
|| (result->_document && !result->_document->isValid()); || (result->_document && result->_document->isNull());
if (!message) { if (!message) {
return nullptr; return nullptr;
@ -246,11 +246,10 @@ std::unique_ptr<Result> Result::create(uint64 queryId, const MTPBotInlineResult
bool Result::onChoose(Layout::ItemBase *layout) { bool Result::onChoose(Layout::ItemBase *layout) {
if (_photo && _type == Type::Photo) { if (_photo && _type == Type::Photo) {
if (_photo->medium->loaded() || _photo->thumb->loaded()) { if (_photo->thumbnail()->loaded()) {
return true; return true;
} else if (!_photo->medium->loading()) { } else if (!_photo->thumbnail()->loading()) {
_photo->thumb->loadEvenCancelled(Data::FileOrigin()); _photo->thumbnail()->loadEvenCancelled(Data::FileOrigin());
_photo->medium->loadEvenCancelled(Data::FileOrigin());
} }
return false; return false;
} }

View File

@ -1640,8 +1640,8 @@ void MainWidget::checkChatBackground() {
}); });
} }
ImagePtr MainWidget::newBackgroundThumb() { Image *MainWidget::newBackgroundThumb() {
return _background ? _background->data.thumb : ImagePtr(); return _background ? _background->data.thumb : nullptr;
} }
void MainWidget::messageDataReceived(ChannelData *channel, MsgId msgId) { void MainWidget::messageDataReceived(ChannelData *channel, MsgId msgId) {

View File

@ -237,7 +237,7 @@ public:
bool chatBackgroundLoading(); bool chatBackgroundLoading();
float64 chatBackgroundProgress() const; float64 chatBackgroundProgress() const;
void checkChatBackground(); void checkChatBackground();
ImagePtr newBackgroundThumb(); Image *newBackgroundThumb();
void messageDataReceived(ChannelData *channel, MsgId msgId); void messageDataReceived(ChannelData *channel, MsgId msgId);
void updateBotKeyboard(History *h); void updateBotKeyboard(History *h);

View File

@ -318,7 +318,7 @@ void Instance::play(const AudioMsgId &audioId) {
} }
} }
if (document->isVoiceMessage() || document->isVideoMessage()) { if (document->isVoiceMessage() || document->isVideoMessage()) {
document->session()->data().markMediaRead(document); document->owner().markMediaRead(document);
} }
} }

View File

@ -129,7 +129,7 @@ public:
Thumb( Thumb(
Key key, Key key,
ImagePtr image, Image *image,
Data::FileOrigin origin, Data::FileOrigin origin,
Fn<void()> handler); Fn<void()> handler);
@ -157,7 +157,7 @@ private:
ClickHandlerPtr _link; ClickHandlerPtr _link;
const Key _key; const Key _key;
ImagePtr _image; Image *_image = nullptr;
Data::FileOrigin _origin; Data::FileOrigin _origin;
State _state = State::Alive; State _state = State::Alive;
QPixmap _full; QPixmap _full;
@ -172,7 +172,7 @@ private:
GroupThumbs::Thumb::Thumb( GroupThumbs::Thumb::Thumb(
Key key, Key key,
ImagePtr image, Image *image,
Data::FileOrigin origin, Data::FileOrigin origin,
Fn<void()> handler) Fn<void()> handler)
: _key(key) : _key(key)
@ -186,15 +186,15 @@ GroupThumbs::Thumb::Thumb(
} }
QSize GroupThumbs::Thumb::wantedPixSize() const { QSize GroupThumbs::Thumb::wantedPixSize() const {
const auto originalWidth = std::max(_image->width(), 1); const auto originalWidth = _image ? std::max(_image->width(), 1) : 1;
const auto originalHeight = std::max(_image->height(), 1); const auto originalHeight = _image ? std::max(_image->height(), 1) : 1;
const auto pixHeight = st::mediaviewGroupHeight; const auto pixHeight = st::mediaviewGroupHeight;
const auto pixWidth = originalWidth * pixHeight / originalHeight; const auto pixWidth = originalWidth * pixHeight / originalHeight;
return { pixWidth, pixHeight }; return { pixWidth, pixHeight };
} }
void GroupThumbs::Thumb::validateImage() { void GroupThumbs::Thumb::validateImage() {
if (!_full.isNull()) { if (!_full.isNull() || !_image) {
return; return;
} }
_image->load(_origin); _image->load(_origin);
@ -516,20 +516,18 @@ auto GroupThumbs::createThumb(Key key)
-> std::unique_ptr<Thumb> { -> std::unique_ptr<Thumb> {
if (const auto photoId = base::get_if<PhotoId>(&key)) { if (const auto photoId = base::get_if<PhotoId>(&key)) {
const auto photo = Auth().data().photo(*photoId); const auto photo = Auth().data().photo(*photoId);
return createThumb( return createThumb(key, photo->thumbnail());
key,
photo->date ? photo->thumb : ImagePtr());
} else if (const auto msgId = base::get_if<FullMsgId>(&key)) { } else if (const auto msgId = base::get_if<FullMsgId>(&key)) {
if (const auto item = App::histItemById(*msgId)) { if (const auto item = App::histItemById(*msgId)) {
if (const auto media = item->media()) { if (const auto media = item->media()) {
if (const auto photo = media->photo()) { if (const auto photo = media->photo()) {
return createThumb(key, photo->thumb); return createThumb(key, photo->thumbnail());
} else if (const auto document = media->document()) { } 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)) { } else if (const auto collageKey = base::get_if<CollageKey>(&key)) {
if (const auto itemId = base::get_if<FullMsgId>(&_context)) { if (const auto itemId = base::get_if<FullMsgId>(&_context)) {
if (const auto item = App::histItemById(*itemId)) { 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()"); Unexpected("Value of Key in GroupThumbs::createThumb()");
} }
@ -554,18 +552,18 @@ auto GroupThumbs::createThumb(
int index) int index)
-> std::unique_ptr<Thumb> { -> std::unique_ptr<Thumb> {
if (index < 0 || index >= collage.items.size()) { if (index < 0 || index >= collage.items.size()) {
return createThumb(key, ImagePtr()); return createThumb(key, nullptr);
} }
const auto &item = collage.items[index]; const auto &item = collage.items[index];
if (const auto photo = base::get_if<PhotoData*>(&item)) { 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)) { } 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> { -> std::unique_ptr<Thumb> {
const auto weak = base::make_weak(this); const auto weak = base::make_weak(this);
const auto origin = ComputeFileOrigin(key, _context); const auto origin = ComputeFileOrigin(key, _context);

View File

@ -99,7 +99,7 @@ private:
Key key, Key key,
const WebPageCollage &collage, const WebPageCollage &collage,
int index); int index);
std::unique_ptr<Thumb> createThumb(Key key, ImagePtr image); std::unique_ptr<Thumb> createThumb(Key key, Image *image);
void update(); void update();
void countUpdatedRect(); void countUpdatedRect();

View File

@ -565,7 +565,7 @@ float64 MediaView::radialProgress() const {
if (_doc) { if (_doc) {
return _doc->progress(); return _doc->progress();
} else if (_photo) { } else if (_photo) {
return _photo->full->progress(); return _photo->large()->progress();
} }
return 1.; return 1.;
} }
@ -574,7 +574,7 @@ bool MediaView::radialLoading() const {
if (_doc) { if (_doc) {
return _doc->loading(); return _doc->loading();
} else if (_photo) { } else if (_photo) {
return _photo->full->loading(); return _photo->large()->loading();
} }
return false; return false;
} }
@ -897,7 +897,7 @@ void MediaView::onSaveAs() {
_photo->date), _photo->date),
crl::guard(this, [this, photo = _photo](const QString &result) { crl::guard(this, [this, photo = _photo](const QString &result) {
if (!result.isEmpty() && _photo == photo && photo->loaded()) { if (!result.isEmpty() && _photo == photo && photo->loaded()) {
photo->full->pix(fileOrigin()).toImage().save(result, "JPG"); photo->large()->original().save(result, "JPG");
} }
psShowOverAll(this); psShowOverAll(this);
}), crl::guard(this, [this] { }), crl::guard(this, [this] {
@ -1019,7 +1019,7 @@ void MediaView::onDownload() {
} else { } else {
if (!QDir().exists(path)) QDir().mkpath(path); if (!QDir().exists(path)) QDir().mkpath(path);
toName = filedialogDefaultName(qsl("photo"), qsl(".jpg"), 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(); toName = QString();
} }
} }
@ -1098,7 +1098,7 @@ void MediaView::onCopy() {
} else { } else {
if (!_photo || !_photo->loaded()) return; 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) { void MediaView::displayPhoto(not_null<PhotoData*> photo, HistoryItem *item) {
if (photo->isNull()) {
displayDocument(nullptr, item);
return;
}
stopGif(); stopGif();
destroyThemePreview(); destroyThemePreview();
_doc = _autoplayVideoDocument = nullptr; _doc = _autoplayVideoDocument = nullptr;
@ -1555,11 +1559,11 @@ void MediaView::displayPhoto(not_null<PhotoData*> photo, HistoryItem *item) {
_zoomToScreen = 0; _zoomToScreen = 0;
Auth().downloader().clearPriorities(); Auth().downloader().clearPriorities();
_full = -1; _blurred = true;
_current = QPixmap(); _current = QPixmap();
_down = OverNone; _down = OverNone;
_w = ConvertScale(photo->full->width()); _w = ConvertScale(photo->width());
_h = ConvertScale(photo->full->height()); _h = ConvertScale(photo->height());
if (isHidden()) { if (isHidden()) {
moveToScreen(); moveToScreen();
} }
@ -1618,10 +1622,10 @@ void MediaView::displayDocument(DocumentData *doc, HistoryItem *item) { // empty
} }
if (_doc) { if (_doc) {
if (_doc->sticker()) { if (_doc->sticker()) {
if (const auto image = _doc->getStickerImage()) { if (const auto image = _doc->getStickerLarge()) {
_current = image->pix(fileOrigin()); _current = image->pix(fileOrigin());
} else { } else if (_doc->hasThumbnail()) {
_current = _doc->thumb->pixBlurred( _current = _doc->thumbnail()->pixBlurred(
fileOrigin(), fileOrigin(),
_doc->dimensions.width(), _doc->dimensions.width(),
_doc->dimensions.height()); _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); _docIconRect = QRect((width() - st::mediaviewFileIconSize) / 2, (height() - st::mediaviewFileIconSize) / 2, st::mediaviewFileIconSize, st::mediaviewFileIconSize);
if (fileBubbleShown()) { if (fileBubbleShown()) {
if (!_doc || _doc->thumb->isNull()) { if (!_doc || !_doc->hasThumbnail()) {
int32 colorIndex = documentColorIndex(_doc, _docExt); int32 colorIndex = documentColorIndex(_doc, _docExt);
_docIconColor = documentColor(colorIndex); _docIconColor = documentColor(colorIndex);
const style::icon *(thumbs[]) = { &st::mediaviewFileBlue, &st::mediaviewFileGreen, &st::mediaviewFileRed, &st::mediaviewFileYellow }; 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); _docExtWidth = st::mediaviewFileExtFont->width(_docExt);
} }
} else { } else {
_doc->thumb->load(fileOrigin()); _doc->loadThumbnail(fileOrigin());
int32 tw = _doc->thumb->width(), th = _doc->thumb->height(); int32 tw = _doc->thumbnail()->width(), th = _doc->thumbnail()->height();
if (!tw || !th) { if (!tw || !th) {
_docThumbx = _docThumby = _docThumbw = 0; _docThumbx = _docThumby = _docThumbw = 0;
} else if (tw > th) { } else if (tw > th) {
@ -1745,7 +1749,7 @@ void MediaView::displayDocument(DocumentData *doc, HistoryItem *item) { // empty
} else { } else {
_from = _user; _from = _user;
} }
_full = 1; _blurred = false;
displayFinished(); displayFinished();
} }
@ -1794,10 +1798,10 @@ void MediaView::initAnimation() {
} else if (_doc->dimensions.width() && _doc->dimensions.height()) { } else if (_doc->dimensions.width() && _doc->dimensions.height()) {
auto w = _doc->dimensions.width(); auto w = _doc->dimensions.width();
auto h = _doc->dimensions.height(); 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()); _current.setDevicePixelRatio(cRetinaFactor());
} else { } 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()) { if (_doc->dimensions.width() && _doc->dimensions.height()) {
int w = _doc->dimensions.width(); int w = _doc->dimensions.width();
int h = _doc->dimensions.height(); 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()); _current.setDevicePixelRatio(cRetinaFactor());
} else { } 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()) auto mode = (_doc->isVideoFile() || _doc->isVideoMessage())
? Media::Clip::Reader::Mode::Video ? Media::Clip::Reader::Mode::Video
@ -2050,6 +2054,38 @@ void MediaView::updateSilentVideoPlaybackState() {
updateVideoPlaybackState(state); 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) { void MediaView::paintEvent(QPaintEvent *e) {
QRect r(e->rect()); QRect r(e->rect());
QRegion region(e->region()); QRegion region(e->region());
@ -2082,39 +2118,24 @@ void MediaView::paintEvent(QPaintEvent *e) {
// photo // photo
if (_photo) { if (_photo) {
int32 w = _width * cIntRetinaFactor(); validatePhotoCurrentImage();
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());
}
} }
p.setOpacity(1); p.setOpacity(1);
if (_photo || fileShown()) { if (_photo || fileShown()) {
QRect imgRect(_x, _y, _w, _h); QRect imgRect(_x, _y, _w, _h);
if (imgRect.intersects(r)) { if (imgRect.intersects(r)) {
auto rounding = (_doc && _doc->isVideoMessage()) ? ImageRoundRadius::Ellipse : ImageRoundRadius::None; const 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; 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->getStickerImage()) && toDraw.hasAlpha()) { if (!_gif && (!_doc || !_doc->getStickerLarge()) && (toDraw.hasAlpha() || toDraw.isNull())) {
p.fillRect(imgRect, _transparentBrush); p.fillRect(imgRect, _transparentBrush);
} }
if (toDraw.width() != _w * cIntRetinaFactor()) { if (!toDraw.isNull()) {
PainterHighQualityEnabler hq(p); if (toDraw.width() != _w * cIntRetinaFactor()) {
p.drawPixmap(QRect(_x, _y, _w, _h), toDraw); PainterHighQualityEnabler hq(p);
} else { p.drawPixmap(QRect(_x, _y, _w, _h), toDraw);
p.drawPixmap(_x, _y, toDraw); } else {
p.drawPixmap(_x, _y, toDraw);
}
} }
bool radial = false; bool radial = false;
@ -2165,7 +2186,7 @@ void MediaView::paintEvent(QPaintEvent *e) {
p.restoreTextPalette(); p.restoreTextPalette();
p.setOpacity(1); p.setOpacity(1);
} }
if (_full >= 1) { if (!_blurred) {
auto nextFrame = (dt < st::mediaviewSaveMsgShowing || hidingDt >= 0) ? int(AnimationTimerDelta) : (st::mediaviewSaveMsgShowing + st::mediaviewSaveMsgShown + 1 - dt); auto nextFrame = (dt < st::mediaviewSaveMsgShowing || hidingDt >= 0) ? int(AnimationTimerDelta) : (st::mediaviewSaveMsgShowing + st::mediaviewSaveMsgShown + 1 - dt);
_saveMsgUpdater.start(nextFrame); _saveMsgUpdater.start(nextFrame);
} }
@ -2187,7 +2208,7 @@ void MediaView::paintEvent(QPaintEvent *e) {
radial = _radial.animating(); radial = _radial.animating();
radialOpacity = _radial.opacity(); radialOpacity = _radial.opacity();
} }
if (!_doc || _doc->thumb->isNull()) { if (!_doc || !_doc->hasThumbnail()) {
p.fillRect(_docIconRect, _docIconColor); p.fillRect(_docIconRect, _docIconColor);
if ((!_doc || _doc->loaded()) && (!radial || radialOpacity < 1) && _docIcon) { if ((!_doc || _doc->loaded()) && (!radial || radialOpacity < 1) && _docIcon) {
_docIcon->paint(p, _docIconRect.x() + (_docIconRect.width() - _docIcon->width()), _docIconRect.y(), width()); _docIcon->paint(p, _docIconRect.x() + (_docIconRect.width() - _docIcon->width()), _docIconRect.y(), width());
@ -2199,7 +2220,7 @@ void MediaView::paintEvent(QPaintEvent *e) {
} }
} else { } else {
int32 rf(cIntRetinaFactor()); 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); paintDocRadialLoading(p, radial, radialOpacity);
@ -2720,10 +2741,10 @@ void MediaView::preloadData(int delta) {
if (auto photo = base::get_if<not_null<PhotoData*>>(&entity.data)) { if (auto photo = base::get_if<not_null<PhotoData*>>(&entity.data)) {
(*photo)->download(fileOrigin()); (*photo)->download(fileOrigin());
} else if (auto document = base::get_if<not_null<DocumentData*>>(&entity.data)) { } 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()); image->load(fileOrigin());
} else { } else {
(*document)->thumb->load(fileOrigin()); (*document)->loadThumbnail(fileOrigin());
(*document)->automaticLoad(fileOrigin(), entity.item); (*document)->automaticLoad(fileOrigin(), entity.item);
} }
} }

View File

@ -251,6 +251,9 @@ private:
void checkGroupThumbsAnimation(); void checkGroupThumbsAnimation();
void initGroupThumbs(); void initGroupThumbs();
void validatePhotoImage(Image *image, bool blurred);
void validatePhotoCurrentImage();
QBrush _transparentBrush; QBrush _transparentBrush;
PhotoData *_photo = nullptr; PhotoData *_photo = nullptr;
@ -299,7 +302,7 @@ private:
int32 _dragging = 0; int32 _dragging = 0;
QPixmap _current; QPixmap _current;
Media::Clip::ReaderPointer _gif; Media::Clip::ReaderPointer _gif;
int32 _full = -1; // -1 - thumb, 0 - medium, 1 - full bool _blurred = true;
// Video without audio stream playback information. // Video without audio stream playback information.
bool _videoIsSilent = false; bool _videoIsSilent = false;

View File

@ -328,33 +328,23 @@ int32 Photo::resizeGetHeight(int32 width) {
void Photo::paint(Painter &p, const QRect &clip, TextSelection selection, const PaintContext *context) { void Photo::paint(Painter &p, const QRect &clip, TextSelection selection, const PaintContext *context) {
bool good = _data->loaded(), selected = (selection == FullSelection); bool good = _data->loaded(), selected = (selection == FullSelection);
if (!good) { if (!good) {
_data->medium->automaticLoad(parent()->fullId(), parent()); _data->thumbnail()->automaticLoad(parent()->fullId(), parent());
good = _data->medium->loaded(); good = _data->thumbnail()->loaded();
} }
if ((good && !_goodLoaded) || _pix.width() != _width * cIntRetinaFactor()) { if ((good && !_goodLoaded) || _pix.width() != _width * cIntRetinaFactor()) {
_goodLoaded = good; _goodLoaded = good;
_pix = QPixmap();
int32 size = _width * cIntRetinaFactor(); if (_goodLoaded) {
if (_goodLoaded || _data->thumb->loaded()) { setPixFrom(_data->loaded()
auto img = (_data->loaded() ? _data->full : (_data->medium->loaded() ? _data->medium : _data->thumb))->pix(parent()->fullId()).toImage(); ? _data->large()
if (!_goodLoaded) { : _data->thumbnail());
img = Images::prepareBlur(std::move(img)); } 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); 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( TextState Photo::getState(
QPoint point, QPoint point,
StateRequest request) const { StateRequest request) const {
@ -387,10 +400,9 @@ Video::Video(
not_null<DocumentData*> video) not_null<DocumentData*> video)
: RadialProgressItem(parent) : RadialProgressItem(parent)
, _data(video) , _data(video)
, _duration(formatDurationText(_data->duration())) , _duration(formatDurationText(_data->duration())) {
, _thumbLoaded(false) {
setDocumentLinks(_data); setDocumentLinks(_data);
_data->thumb->load(parent->fullId()); _data->loadThumbnail(parent->fullId());
} }
void Video::initDimensions() { 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) { 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()); _data->automaticLoad(parent()->fullId(), parent());
bool loaded = _data->loaded(), displayLoading = _data->displayLoading(); bool loaded = _data->loaded(), displayLoading = _data->displayLoading();
@ -418,28 +433,25 @@ void Video::paint(Painter &p, const QRect &clip, TextSelection selection, const
updateStatusText(); updateStatusText();
bool radial = isRadialAnimation(context->ms); bool radial = isRadialAnimation(context->ms);
if ((thumbLoaded && !_thumbLoaded) || (_pix.width() != _width * cIntRetinaFactor())) { if ((blurred || thumbLoaded)
_thumbLoaded = thumbLoaded; && (_pix.width() != _width * cIntRetinaFactor())) {
auto size = _width * cIntRetinaFactor();
if (_thumbLoaded && !_data->thumb->isNull()) { auto img = thumbLoaded
auto size = _width * cIntRetinaFactor(); ? _data->thumbnail()->original()
auto img = Images::prepareBlur(_data->thumb->pix(parent()->fullId()).toImage()); : Images::prepareBlur(blurred->original());
if (img.width() == img.height()) { if (img.width() == img.height()) {
if (img.width() != size) { if (img.width() != size) {
img = img.scaled(size, size, Qt::KeepAspectRatioByExpanding, Qt::SmoothTransformation); 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()); } else if (img.width() > img.height()) {
_data->unload(); img = img.copy((img.width() - img.height()) / 2, 0, img.height(), img.height()).scaled(size, size, Qt::KeepAspectRatioByExpanding, Qt::SmoothTransformation);
} else {
_pix = App::pixmapFromImageInPlace(std::move(img)); img = img.copy(0, (img.height() - img.width()) / 2, img.width(), img.width()).scaled(size, size, Qt::KeepAspectRatioByExpanding, Qt::SmoothTransformation);
} else if (!_pix.isNull()) {
_pix = QPixmap();
} }
img.setDevicePixelRatio(cRetinaFactor());
_data->unload();
_pix = App::pixmapFromImageInPlace(std::move(img));
} }
if (_pix.isNull()) { if (_pix.isNull()) {
@ -582,7 +594,7 @@ Voice::Voice(
AddComponents(Info::Bit()); AddComponents(Info::Bit());
setDocumentLinks(_data); setDocumentLinks(_data);
_data->thumb->load(parent->fullId()); _data->loadThumbnail(parent->fullId());
updateName(); updateName();
const auto dateText = textcmdLink( const auto dateText = textcmdLink(
@ -641,22 +653,28 @@ void Voice::paint(Painter &p, const QRect &clip, TextSelection selection, const
_width); _width);
if (clip.intersects(inner)) { if (clip.intersects(inner)) {
p.setPen(Qt::NoPen); p.setPen(Qt::NoPen);
const auto drawThumb = !_data->thumb->isNull() const auto thumbLoaded = _data->hasThumbnail()
&& _data->thumb->loaded(); && _data->thumbnail()->loaded();
if (drawThumb) { const auto blurred = _data->thumbnailInline();
const auto thumb = _data->thumb->pixCircled( if (thumbLoaded || blurred) {
parent()->fullId(), const auto thumb = thumbLoaded
inner.width(), ? _data->thumbnail()->pixCircled(
inner.height()); parent()->fullId(),
inner.width(),
inner.height())
: blurred->pixBlurredCircled(
parent()->fullId(),
inner.width(),
inner.height());
p.drawPixmap(inner.topLeft(), thumb); p.drawPixmap(inner.topLeft(), thumb);
} else if (!_data->thumb->isNull()) { } else if (_data->hasThumbnail()) {
PainterHighQualityEnabler hq(p); PainterHighQualityEnabler hq(p);
p.setBrush(st::imageBg); p.setBrush(st::imageBg);
p.drawEllipse(inner); p.drawEllipse(inner);
} }
if (selected) { if (selected) {
p.setBrush(drawThumb ? st::msgDateImgBgSelected : st::msgFileInBgSelected); p.setBrush((thumbLoaded || blurred) ? st::msgDateImgBgSelected : st::msgFileInBgSelected);
} else if (!_data->thumb->isNull()) { } else if (_data->hasThumbnail()) {
auto over = ClickHandler::showAsActive(loaded ? _openl : (_data->loading() ? _cancell : _openl)); 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.))); p.setBrush(anim::brush(st::msgDateImgBg, st::msgDateImgBgOver, _a_iconOver.current(context->ms, over ? 1. : 0.)));
} else { } else {
@ -868,8 +886,8 @@ Document::Document(
_status.update(FileStatusSizeReady, _data->size, _data->isSong() ? _data->song()->duration : -1, 0); _status.update(FileStatusSizeReady, _data->size, _data->isSong() ? _data->song()->duration : -1, 0);
if (withThumb()) { if (withThumb()) {
_data->thumb->load(parent->fullId()); _data->loadThumbnail(parent->fullId());
int32 tw = ConvertScale(_data->thumb->width()), th = ConvertScale(_data->thumb->height()); int32 tw = ConvertScale(_data->thumbnail()->width()), th = ConvertScale(_data->thumbnail()->height());
if (tw > th) { if (tw > th) {
_thumbw = (tw * _st.fileThumbSize) / th; _thumbw = (tw * _st.fileThumbSize) / th;
} else { } 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)); QRect rthumb(rtlrect(0, st::linksBorder + _st.filePadding.top(), _st.fileThumbSize, _st.fileThumbSize, _width));
if (clip.intersects(rthumb)) { if (clip.intersects(rthumb)) {
if (wthumb) { if (wthumb) {
if (_data->thumb->loaded()) { const auto thumbLoaded = _data->thumbnail()->loaded();
if (_thumb.isNull() || loaded != _thumbForLoaded) { const auto blurred = _data->thumbnailInline();
_thumbForLoaded = loaded; if (thumbLoaded || blurred) {
if (_thumb.isNull() || (thumbLoaded && !_thumbLoaded)) {
_thumbLoaded = thumbLoaded;
auto options = Images::Option::Smooth | Images::Option::None; auto options = Images::Option::Smooth | Images::Option::None;
if (!_thumbForLoaded) options |= Images::Option::Blurred; if (!_thumbLoaded) options |= Images::Option::Blurred;
_thumb = _data->thumb->pixNoCache(parent()->fullId(), _thumbw * cIntRetinaFactor(), 0, options, _st.fileThumbSize, _st.fileThumbSize); _thumb = (_thumbLoaded
? _data->thumbnail()
: blurred)->pixNoCache(parent()->fullId(), _thumbw * cIntRetinaFactor(), 0, options, _st.fileThumbSize, _st.fileThumbSize);
} }
p.drawPixmap(rthumb.topLeft(), _thumb); p.drawPixmap(rthumb.topLeft(), _thumb);
} else { } else {
@ -1132,7 +1154,7 @@ TextState Document::getState(
return { parent(), _msgl }; return { parent(), _msgl };
} }
} }
if (!_data->loading() && _data->isValid()) { if (!_data->loading() && !_data->isNull()) {
auto leftofnamerect = rtlrect( auto leftofnamerect = rtlrect(
0, 0,
st::linksBorder, st::linksBorder,
@ -1180,9 +1202,9 @@ bool Document::iconAnimated() const {
bool Document::withThumb() const { bool Document::withThumb() const {
return !_data->isSong() return !_data->isSong()
&& !_data->thumb->isNull() && _data->hasThumbnail()
&& _data->thumb->width() && _data->thumbnail()->width()
&& _data->thumb->height() && _data->thumbnail()->height()
&& !Data::IsExecutableName(_data->filename()); && !Data::IsExecutableName(_data->filename());
} }
@ -1304,19 +1326,19 @@ Link::Link(
} }
int32 tw = 0, th = 0; int32 tw = 0, th = 0;
if (_page && _page->photo) { if (_page && _page->photo) {
if (!_page->photo->loaded()) { if (!_page->photo->loaded()
_page->photo->thumb->load(parent->fullId(), false, false); && !_page->photo->thumbnail()->loaded()
&& !_page->photo->thumbnailSmall()->loaded()) {
_page->photo->loadThumbnailSmall(parent->fullId());
} }
tw = ConvertScale(_page->photo->thumb->width()); tw = ConvertScale(_page->photo->width());
th = ConvertScale(_page->photo->thumb->height()); th = ConvertScale(_page->photo->height());
} else if (_page && _page->document) { } else if (_page && _page->document && _page->document->hasThumbnail()) {
if (!_page->document->thumb->loaded()) { _page->document->loadThumbnail(parent->fullId());
_page->document->thumb->load(parent->fullId(), false, false);
}
tw = ConvertScale(_page->document->thumb->width()); tw = ConvertScale(_page->document->thumbnail()->width());
th = ConvertScale(_page->document->thumb->height()); th = ConvertScale(_page->document->thumbnail()->height());
} }
if (tw > st::linksPhotoSize) { if (tw > st::linksPhotoSize) {
if (th > tw) { 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 (clip.intersects(rtlrect(0, pixTop, st::linksPhotoSize, st::linksPhotoSize, _width))) {
if (_page && _page->photo) { if (_page && _page->photo) {
QPixmap pix; QPixmap pix;
if (_page->photo->medium->loaded()) { if (_page->photo->thumbnail()->loaded()) {
pix = _page->photo->medium->pixSingle(parent()->fullId(), _pixw, _pixh, st::linksPhotoSize, st::linksPhotoSize, ImageRoundRadius::Small); pix = _page->photo->thumbnail()->pixSingle(parent()->fullId(), _pixw, _pixh, st::linksPhotoSize, st::linksPhotoSize, ImageRoundRadius::Small);
} else if (_page->photo->loaded()) { } else if (_page->photo->loaded()) {
pix = _page->photo->full->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 { } else if (_page->photo->thumbnailSmall()->loaded()) {
pix = _page->photo->thumb->pixSingle(parent()->fullId(), _pixw, _pixh, st::linksPhotoSize, st::linksPhotoSize, ImageRoundRadius::Small); 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); 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() auto roundRadius = _page->document->isVideoMessage()
? ImageRoundRadius::Ellipse ? ImageRoundRadius::Ellipse
: ImageRoundRadius::Small; : 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 { } else {
const auto index = _letter.isEmpty() const auto index = _letter.isEmpty()
? 0 ? 0

View File

@ -208,6 +208,8 @@ public:
StateRequest request) const override; StateRequest request) const override;
private: private:
void setPixFrom(not_null<Image*> image);
not_null<PhotoData*> _data; not_null<PhotoData*> _data;
ClickHandlerPtr _link; ClickHandlerPtr _link;
@ -241,7 +243,6 @@ private:
QString _duration; QString _duration;
QPixmap _pix; QPixmap _pix;
bool _thumbLoaded = false;
void updateStatusText(); void updateStatusText();
@ -315,7 +316,7 @@ private:
const style::OverviewFileLayout &_st; const style::OverviewFileLayout &_st;
bool _thumbForLoaded = false; bool _thumbLoaded = false;
QPixmap _thumb; QPixmap _thumb;
Text _name; Text _name;

View File

@ -146,7 +146,7 @@ void BackgroundRow::paintEvent(QPaintEvent *e) {
} }
if (radial) { if (radial) {
const auto backThumb = App::main()->newBackgroundThumb(); const auto backThumb = App::main()->newBackgroundThumb();
if (backThumb->isNull()) { if (!backThumb) {
p.drawPixmap(0, 0, _background); p.drawPixmap(0, 0, _background);
} else { } else {
const auto &pix = backThumb->pixBlurred( const auto &pix = backThumb->pixBlurred(

View File

@ -150,11 +150,11 @@ void Uploader::uploadMedia(
Auth().data().processPhoto(media.photo, media.photoThumbs); Auth().data().processPhoto(media.photo, media.photoThumbs);
} else if (media.type == SendMediaType::File } else if (media.type == SendMediaType::File
|| media.type == SendMediaType::Audio) { || 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)
: Auth().data().processDocument( : Auth().data().processDocument(
media.document, media.document,
base::duplicate(media.photoThumbs.begin().value())); base::duplicate(media.photoThumbs.front().second));
if (!media.data.isEmpty()) { if (!media.data.isEmpty()) {
document->setData(media.data); document->setData(media.data);
if (document->saveToCache() if (document->saveToCache()

View File

@ -198,7 +198,7 @@ SendMediaReady PreparePeerPhoto(PeerId peerId, QImage &&image) {
MTP_long(0)), MTP_long(0)),
MTP_int(image.width()), MTP_int(image.width()),
MTP_int(image.height()), MTP_int(0))); 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("a", scaled(160));
push("b", scaled(320)); push("b", scaled(320));
@ -795,15 +795,15 @@ void FileLoadTask::process() {
attributes.push_back(MTP_documentAttributeAnimated()); attributes.push_back(MTP_documentAttributeAnimated());
} else if (_type != SendMediaType::File) { } else if (_type != SendMediaType::File) {
auto thumb = (w > 100 || h > 100) ? fullimage.scaled(100, 100, Qt::KeepAspectRatio, Qt::SmoothTransformation) : fullimage; 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))); 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; 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))); 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; 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))); 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)));
{ {

View File

@ -3753,6 +3753,7 @@ void importOldRecentStickers() {
attributes, attributes,
mime, mime,
ImagePtr(), ImagePtr(),
ImagePtr(),
dc, dc,
size, size,
StorageImageLocation()); StorageImageLocation());

View File

@ -49,7 +49,11 @@ void Document::writeToStream(QDataStream &stream, DocumentData *document) {
writeStorageImageLocation(stream, document->sticker()->loc); writeStorageImageLocation(stream, document->sticker()->loc);
} else { } else {
stream << qint32(document->duration()); 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, date,
attributes, attributes,
mime, mime,
ImagePtr(),
thumb.isNull() ? ImagePtr() : Images::Create(thumb), thumb.isNull() ? ImagePtr() : Images::Create(thumb),
dc, dc,
size, size,
@ -172,7 +177,11 @@ int Document::sizeInStream(DocumentData *document) {
// + duration // + duration
result += sizeof(qint32); result += sizeof(qint32);
// + thumb loc // + 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; return result;

View File

@ -755,7 +755,9 @@ QImage Image::original() const {
return _data; return _data;
} }
void Image::automaticLoad(Data::FileOrigin origin, const HistoryItem *item) { void Image::automaticLoad(
Data::FileOrigin origin,
const HistoryItem *item) {
if (!loaded()) { if (!loaded()) {
_source->automaticLoad(origin, item); _source->automaticLoad(origin, item);
} }

View File

@ -198,6 +198,9 @@ public:
int height() const { int height() const {
return _source->height(); return _source->height();
} }
QSize size() const {
return { width(), height() };
}
int bytesSize() const { int bytesSize() const {
return _source->bytesSize(); return _source->bytesSize();
} }

View File

@ -49,6 +49,9 @@ QPixmap PixmapFast(QImage &&image) {
} }
QImage prepareBlur(QImage img) { QImage prepareBlur(QImage img) {
if (img.isNull()) {
return img;
}
auto ratio = img.devicePixelRatio(); auto ratio = img.devicePixelRatio();
auto fmt = img.format(); auto fmt = img.format();
if (fmt != QImage::Format_RGB32 && fmt != QImage::Format_ARGB32_Premultiplied) { if (fmt != QImage::Format_RGB32 && fmt != QImage::Format_ARGB32_Premultiplied) {

View File

@ -892,11 +892,6 @@ void MediaPreviewWidget::showPreview(
void MediaPreviewWidget::showPreview( void MediaPreviewWidget::showPreview(
Data::FileOrigin origin, Data::FileOrigin origin,
not_null<PhotoData*> photo) { not_null<PhotoData*> photo) {
if (photo->full->isNull()) {
hidePreview();
return;
}
startShow(); startShow();
_origin = origin; _origin = origin;
_photo = photo; _photo = photo;
@ -965,7 +960,7 @@ QSize MediaPreviewWidget::currentDimensions() const {
QSize result, box; QSize result, box;
if (_photo) { 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); box = QSize(width() - 2 * st::boxVerticalMargin, height() - 2 * st::boxVerticalMargin);
} else { } else {
result = _document->dimensions; result = _document->dimensions;
@ -997,13 +992,15 @@ QPixmap MediaPreviewWidget::currentImage() const {
if (_document) { if (_document) {
if (_document->sticker()) { if (_document->sticker()) {
if (_cacheStatus != CacheLoaded) { if (_cacheStatus != CacheLoaded) {
if (const auto image = _document->getStickerImage()) { if (const auto image = _document->getStickerLarge()) {
QSize s = currentDimensions(); QSize s = currentDimensions();
_cache = image->pix(_origin, s.width(), s.height()); _cache = image->pix(_origin, s.width(), s.height());
_cacheStatus = CacheLoaded; _cacheStatus = CacheLoaded;
} else if (_cacheStatus != CacheThumbLoaded && _document->thumb->loaded()) { } else if (_cacheStatus != CacheThumbLoaded
&& _document->hasThumbnail()
&& _document->thumbnail()->loaded()) {
QSize s = currentDimensions(); QSize s = currentDimensions();
_cache = _document->thumb->pixBlurred(_origin, s.width(), s.height()); _cache = _document->thumbnail()->pixBlurred(_origin, s.width(), s.height());
_cacheStatus = CacheThumbLoaded; _cacheStatus = CacheThumbLoaded;
} }
} }
@ -1023,26 +1020,43 @@ QPixmap MediaPreviewWidget::currentImage() const {
auto paused = _controller->isGifPausedAtLeastFor(Window::GifPauseReason::MediaPreview); 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()); 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(); QSize s = currentDimensions();
_cache = _document->thumb->pixBlurred(_origin, s.width(), s.height()); if (_document->thumbnail()->loaded()) {
_cacheStatus = CacheThumbLoaded; _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) { } else if (_photo) {
if (_cacheStatus != CacheLoaded) { if (_cacheStatus != CacheLoaded) {
if (_photo->full->loaded()) { if (_photo->loaded()) {
QSize s = currentDimensions(); QSize s = currentDimensions();
_cache = _photo->full->pix(_origin, s.width(), s.height()); _cache = _photo->large()->pix(_origin, s.width(), s.height());
_cacheStatus = CacheLoaded; _cacheStatus = CacheLoaded;
} else { } else {
if (_cacheStatus != CacheThumbLoaded && _photo->thumb->loaded()) { _photo->load(_origin);
if (_cacheStatus != CacheThumbLoaded) {
QSize s = currentDimensions(); QSize s = currentDimensions();
_cache = _photo->thumb->pixBlurred(_origin, s.width(), s.height()); if (_photo->thumbnail()->loaded()) {
_cacheStatus = CacheThumbLoaded; _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);
} }
} }

View File

@ -7,6 +7,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/ */
#pragma once #pragma once
class Image;
namespace Data { namespace Data {
struct WallPaper { struct WallPaper {
@ -14,7 +16,7 @@ struct WallPaper {
uint64 accessHash = 0; uint64 accessHash = 0;
MTPDwallPaper::Flags flags; MTPDwallPaper::Flags flags;
QString slug; QString slug;
ImagePtr thumb; Image *thumb = nullptr;
DocumentData *document = nullptr; DocumentData *document = nullptr;
}; };