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.vflags.v,
qs(data.vslug),
document->thumb,
document->thumbnail(),
document
});
}

View File

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

View File

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

View File

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

View File

@ -472,7 +472,7 @@ void StickerSetBox::Inner::paintEvent(QPaintEvent *e) {
p.setOpacity(1);
}
doc->checkStickerThumb();
doc->checkStickerSmall();
float64 coef = qMin((st::stickersSize.width() - st::buttonRadius * 2) / float64(doc->dimensions.width()), (st::stickersSize.height() - st::buttonRadius * 2) / float64(doc->dimensions.height()));
if (coef > 1) coef = 1;
@ -480,7 +480,7 @@ void StickerSetBox::Inner::paintEvent(QPaintEvent *e) {
if (w < 1) w = 1;
if (h < 1) h = 1;
QPoint ppos = pos + QPoint((st::stickersSize.width() - w) / 2, (st::stickersSize.height() - h) / 2);
if (const auto image = doc->getStickerThumb()) {
if (const auto image = doc->getStickerSmall()) {
p.drawPixmapLeft(
ppos,
width(),

View File

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

View File

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

View File

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

View File

@ -561,7 +561,7 @@ void FieldAutocompleteInner::paintEvent(QPaintEvent *e) {
App::roundRect(p, QRect(tl, st::stickerPanSize), st::emojiPanHover, StickerHoverCorners);
}
document->checkStickerThumb();
document->checkStickerSmall();
float64 coef = qMin((st::stickerPanSize.width() - st::buttonRadius * 2) / float64(document->dimensions.width()), (st::stickerPanSize.height() - st::buttonRadius * 2) / float64(document->dimensions.height()));
if (coef > 1) coef = 1;
@ -569,7 +569,7 @@ void FieldAutocompleteInner::paintEvent(QPaintEvent *e) {
if (w < 1) w = 1;
if (h < 1) h = 1;
QPoint ppos = pos + QPoint((st::stickerPanSize.width() - w) / 2, (st::stickerPanSize.height() - h) / 2);
if (const auto image = document->getStickerThumb()) {
if (const auto image = document->getStickerSmall()) {
p.drawPixmapLeft(ppos, width(), image->pix(document->stickerSetOrigin(), w, h));
}
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -8,6 +8,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "data/data_types.h"
#include "data/data_document.h"
#include "data/data_file_origin.h"
#include "ui/image/image_source.h"
#include "ui/widgets/input_fields.h"
#include "storage/cache/storage_cache_types.h"
#include "base/openssl_help.h"
@ -30,6 +32,20 @@ constexpr auto kGeoPointCacheMask = 0x000000FFFFFFFFFFULL;
} // namespace
struct ReplyPreview::Data {
Data(std::unique_ptr<Images::Source> &&source, bool good);
Image image;
bool good = false;
};
ReplyPreview::Data::Data(
std::unique_ptr<Images::Source> &&source,
bool good)
: image(std::move(source))
, good(good) {
}
Storage::Cache::Key DocumentCacheKey(int32 dcId, uint64 id) {
return Storage::Cache::Key{
Data::kDocumentCacheTag | (uint64(dcId) & Data::kDocumentCacheMask),
@ -98,6 +114,63 @@ Storage::Cache::Key GeoPointCacheKey(const GeoPointLocation &location) {
};
}
ReplyPreview::ReplyPreview() = default;
ReplyPreview::ReplyPreview(ReplyPreview &&other) = default;
ReplyPreview &ReplyPreview::operator=(ReplyPreview &&other) = default;
ReplyPreview::~ReplyPreview() = default;
void ReplyPreview::prepare(
not_null<Image*> image,
FileOrigin origin,
Images::Options options) {
int w = image->width(), h = image->height();
if (w <= 0) w = 1;
if (h <= 0) h = 1;
auto thumbSize = (w > h)
? QSize(
w * st::msgReplyBarSize.height() / h,
st::msgReplyBarSize.height())
: QSize(
st::msgReplyBarSize.height(),
h * st::msgReplyBarSize.height() / w);
thumbSize *= cIntRetinaFactor();
const auto prepareOptions = Images::Option::Smooth
| Images::Option::TransparentBackground
| options;
auto outerSize = st::msgReplyBarSize.height();
auto bitmap = image->pixNoCache(
origin,
thumbSize.width(),
thumbSize.height(),
prepareOptions,
outerSize,
outerSize);
_data = std::make_unique<ReplyPreview::Data>(
std::make_unique<Images::ImageSource>(
bitmap.toImage(),
"PNG"),
((options & Images::Option::Blurred) == 0));
}
void ReplyPreview::clear() {
_data = nullptr;
}
Image *ReplyPreview::image() const {
return _data ? &_data->image : nullptr;
}
bool ReplyPreview::good() const {
return !empty() && _data->good;
}
bool ReplyPreview::empty() const {
return !_data;
}
} // namespace Data
void AudioMsgId::setTypeFromAudio() {

View File

@ -23,6 +23,11 @@ namespace Ui {
class InputField;
} // namespace Ui
namespace Images {
enum class Option;
using Options = base::flags<Option>;
} // namespace Images
class StorageImageLocation;
class WebFileLocation;
struct GeoPointLocation;
@ -50,6 +55,35 @@ constexpr auto kVoiceMessageCacheTag = uint8(0x03);
constexpr auto kVideoMessageCacheTag = uint8(0x04);
constexpr auto kAnimationCacheTag = uint8(0x05);
struct FileOrigin;
class ReplyPreview {
public:
ReplyPreview();
ReplyPreview(ReplyPreview &&other);
ReplyPreview &operator=(ReplyPreview &&other);
~ReplyPreview();
void prepare(
not_null<Image*> image,
FileOrigin origin,
Images::Options options);
void clear();
[[nodiscard]] Image *image() const;
[[nodiscard]] bool good() const;
[[nodiscard]] bool empty() const;
[[nodiscard]] explicit operator bool() const {
return !empty();
}
private:
struct Data;
std::unique_ptr<Data> _data;
};
} // namespace Data
struct MessageGroupId {
@ -273,7 +307,7 @@ using PollId = uint64;
using WallPaperId = uint64;
constexpr auto CancelledWebPageId = WebPageId(0xFFFFFFFFFFFFFFFFULL);
using PreparedPhotoThumbs = QMap<char, QImage>;
using PreparedPhotoThumbs = base::flat_map<char, QImage>;
// [0] == -1 -- counting, [0] == -2 -- could not count
using VoiceWaveform = QVector<signed char>;

View File

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

View File

@ -1074,7 +1074,7 @@ void InnerWidget::showContextMenu(QContextMenuEvent *e, bool showFromTouch) {
}
void InnerWidget::savePhotoToFile(PhotoData *photo) {
if (!photo || !photo->date || !photo->loaded()) {
if (!photo || photo->isNull() || !photo->loaded()) {
return;
}
@ -1086,7 +1086,7 @@ void InnerWidget::savePhotoToFile(PhotoData *photo) {
filedialogDefaultName(qsl("photo"), qsl(".jpg")),
crl::guard(this, [=](const QString &result) {
if (!result.isEmpty()) {
photo->full->pix(Data::FileOrigin()).toImage().save(result, "JPG");
photo->large()->original().save(result, "JPG");
}
}));
}
@ -1096,9 +1096,9 @@ void InnerWidget::saveDocumentToFile(DocumentData *document) {
}
void InnerWidget::copyContextImage(PhotoData *photo) {
if (!photo || !photo->date || !photo->loaded()) return;
if (!photo || photo->isNull() || !photo->loaded()) return;
QApplication::clipboard()->setPixmap(photo->full->pix(Data::FileOrigin()));
QApplication::clipboard()->setImage(photo->large()->original());
}
void InnerWidget::copySelectedText() {

View File

@ -1771,7 +1771,7 @@ void HistoryInner::copySelectedText() {
}
void HistoryInner::savePhotoToFile(not_null<PhotoData*> photo) {
if (!photo->date || !photo->loaded()) return;
if (photo->isNull() || !photo->loaded()) return;
auto filter = qsl("JPEG Image (*.jpg);;") + FileDialog::AllFilesFilter();
FileDialog::GetWritePath(
@ -1783,15 +1783,15 @@ void HistoryInner::savePhotoToFile(not_null<PhotoData*> photo) {
qsl(".jpg")),
crl::guard(this, [=](const QString &result) {
if (!result.isEmpty()) {
photo->full->pix(Data::FileOrigin()).toImage().save(result, "JPG");
photo->large()->original().save(result, "JPG");
}
}));
}
void HistoryInner::copyContextImage(not_null<PhotoData*> photo) {
if (!photo->date || !photo->loaded()) return;
if (photo->isNull() || !photo->loaded()) return;
QApplication::clipboard()->setPixmap(photo->full->pix(Data::FileOrigin()));
QApplication::clipboard()->setImage(photo->large()->original());
}
void HistoryInner::showStickerPackInfo(not_null<DocumentData*> document) {

View File

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

View File

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

View File

@ -56,7 +56,7 @@ HistoryGif::HistoryGif(
setStatusSize(FileStatusSizeReady);
_caption = createCaption(item);
_data->thumb->load(item->fullId());
_data->loadThumbnail(item->fullId());
}
QSize HistoryGif::countOptimalSize() {
@ -89,8 +89,8 @@ QSize HistoryGif::countOptimalSize() {
} else {
tw = ConvertScale(_data->dimensions.width()), th = ConvertScale(_data->dimensions.height());
if (!tw || !th) {
tw = ConvertScale(_data->thumb->width());
th = ConvertScale(_data->thumb->height());
tw = ConvertScale(_data->thumbnail()->width());
th = ConvertScale(_data->thumbnail()->height());
}
}
const auto maxSize = _data->isVideoMessage()
@ -147,8 +147,8 @@ QSize HistoryGif::countCurrentSize(int newWidth) {
} else {
tw = ConvertScale(_data->dimensions.width()), th = ConvertScale(_data->dimensions.height());
if (!tw || !th) {
tw = ConvertScale(_data->thumb->width());
th = ConvertScale(_data->thumb->height());
tw = ConvertScale(_data->thumbnail()->width());
th = ConvertScale(_data->thumbnail()->height());
}
}
const auto maxSize = _data->isVideoMessage()
@ -347,7 +347,13 @@ void HistoryGif::draw(Painter &p, const QRect &r, TextSelection selection, TimeM
if (good) {
good->load({});
}
p.drawPixmap(rthumb.topLeft(), _data->thumb->pixBlurredSingle(_realParent->fullId(), _thumbw, _thumbh, usew, painth, roundRadius, roundCorners));
if (const auto normal = _data->thumbnail()) {
if (normal->loaded()) {
p.drawPixmap(rthumb.topLeft(), _data->thumbnail()->pixSingle(_realParent->fullId(), _thumbw, _thumbh, usew, painth, roundRadius, roundCorners));
} else if (const auto blurred = _data->thumbnailInline()) {
p.drawPixmap(rthumb.topLeft(), blurred->pixBlurredSingle(_realParent->fullId(), _thumbw, _thumbh, usew, painth, roundRadius, roundCorners));
}
}
}
}

View File

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

View File

@ -30,8 +30,8 @@ HistorySticker::HistorySticker(
: HistoryMedia(parent)
, _data(document)
, _emoji(_data->sticker()->alt) {
_data->thumb->load(parent->data()->fullId());
if (auto emoji = Ui::Emoji::Find(_emoji)) {
_data->loadThumbnail(parent->data()->fullId());
if (const auto emoji = Ui::Emoji::Find(_emoji)) {
_emoji = emoji->text();
}
}
@ -93,7 +93,7 @@ void HistorySticker::draw(Painter &p, const QRect &r, TextSelection selection, T
if (width() < st::msgPadding.left() + st::msgPadding.right() + 1) return;
_data->checkSticker();
_data->checkStickerLarge();
bool loaded = _data->loaded();
bool selected = (selection == FullSelection);
@ -117,14 +117,25 @@ void HistorySticker::draw(Painter &p, const QRect &r, TextSelection selection, T
const auto w = _pixw;
const auto h = _pixh;
const auto &c = st::msgStickerOverlay;
if (const auto image = _data->getStickerImage()) {
if (const auto image = _data->getStickerLarge()) {
return selected
? image->pixColored(o, c, w, h)
: image->pix(o, w, h);
//
// Inline thumbnails can't have alpha channel.
//
//} else if (const auto blurred = _data->thumbnailInline()) {
// return selected
// ? blurred->pixBlurredColored(o, c, w, h)
// : blurred->pixBlurred(o, w, h);
} else if (const auto thumbnail = _data->thumbnail()) {
return selected
? thumbnail->pixBlurredColored(o, c, w, h)
: thumbnail->pixBlurred(o, w, h);
} else {
static QPixmap empty;
return empty;
}
return selected
? _data->thumb->pixBlurredColored(o, c, w, h)
: _data->thumb->pixBlurred(o, w, h);
}();
p.drawPixmap(
QPoint{ usex + (usew - _pixw) / 2, (minHeight() - _pixh) / 2 },

View File

@ -41,7 +41,21 @@ HistoryVideo::HistoryVideo(
setStatusSize(FileStatusSizeReady);
_data->thumb->load(realParent->fullId());
_data->loadThumbnail(realParent->fullId());
}
QSize HistoryVideo::sizeForAspectRatio() const {
// We use size only for aspect ratio and we want to have it
// as close to the thumbnail as possible.
//if (!_data->dimensions.isEmpty()) {
// return _data->dimensions;
//}
if (const auto thumb = _data->thumbnail()) {
if (!thumb->size().isEmpty()) {
return thumb->size();
}
}
return { 1, 1 };
}
QSize HistoryVideo::countOptimalSize() {
@ -53,8 +67,9 @@ QSize HistoryVideo::countOptimalSize() {
_parent->skipBlockHeight());
}
auto tw = ConvertScale(_data->thumb->width());
auto th = ConvertScale(_data->thumb->height());
const auto size = sizeForAspectRatio();
auto tw = ConvertScale(size.width());
auto th = ConvertScale(size.height());
if (!tw || !th) {
tw = th = 1;
}
@ -86,7 +101,9 @@ QSize HistoryVideo::countOptimalSize() {
}
QSize HistoryVideo::countCurrentSize(int newWidth) {
int tw = ConvertScale(_data->thumb->width()), th = ConvertScale(_data->thumb->height());
const auto size = sizeForAspectRatio();
auto tw = ConvertScale(size.width());
auto th = ConvertScale(size.height());
if (!tw || !th) {
tw = th = 1;
}
@ -166,7 +183,12 @@ void HistoryVideo::draw(Painter &p, const QRect &r, TextSelection selection, Tim
if (good) {
good->load({});
}
p.drawPixmap(rthumb.topLeft(), _data->thumb->pixBlurredSingle(_realParent->fullId(), _thumbw, _thumbh, paintw, painth, roundRadius, roundCorners));
if (const auto normal = _data->thumbnail()) {
const auto use = (normal->loaded() || !_data->thumbnailInline())
? normal
: _data->thumbnailInline();
p.drawPixmap(rthumb.topLeft(), use->pixBlurredSingle(_realParent->fullId(), _thumbw, _thumbh, paintw, painth, roundRadius, roundCorners));
}
}
if (selected) {
App::complexOverlayRect(p, rthumb, roundRadius, roundCorners);
@ -288,13 +310,7 @@ TextState HistoryVideo::textState(QPoint point, StateRequest request) const {
}
QSize HistoryVideo::sizeForGrouping() const {
const auto width = _data->dimensions.isEmpty()
? _data->thumb->width()
: _data->dimensions.width();
const auto height = _data->dimensions.isEmpty()
? _data->thumb->height()
: _data->dimensions.height();
return { std::max(width, 1), std::max(height, 1) };
return sizeForAspectRatio();
}
void HistoryVideo::drawGrouped(
@ -384,7 +400,6 @@ void HistoryVideo::drawGrouped(
p.setOpacity(backOpacity);
if (icon) {
if (previous && radialOpacity > 0. && radialOpacity < 1.) {
LOG(("INTERPOLATING: %1").arg(radialOpacity));
PaintInterpolatedIcon(p, *icon, *previous, radialOpacity, inner);
} else {
icon->paintInCenter(p, inner);
@ -442,13 +457,18 @@ void HistoryVideo::validateGroupedCache(
using Option = Images::Option;
const auto good = _data->goodThumbnail();
const auto useGood = (good && good->loaded());
const auto image = useGood ? good : _data->thumb.get();
const auto thumb = _data->thumbnail();
const auto useThumb = (thumb && thumb->loaded());
const auto image = useGood
? good
: useThumb
? thumb
: _data->thumbnailInline();
if (good && !useGood) {
good->load({});
}
const auto loaded = useGood ? true : _data->thumb->loaded();
const auto loadLevel = loaded ? 1 : 0;
const auto loadLevel = useGood ? 3 : useThumb ? 2 : image ? 1 : 0;
const auto width = geometry.width();
const auto height = geometry.height();
const auto options = Option::Smooth
@ -466,8 +486,9 @@ void HistoryVideo::validateGroupedCache(
return;
}
const auto originalWidth = ConvertScale(_data->thumb->width());
const auto originalHeight = ConvertScale(_data->thumb->height());
const auto original = sizeForAspectRatio();
const auto originalWidth = ConvertScale(original.width());
const auto originalHeight = ConvertScale(original.height());
const auto pixSize = Ui::GetImageScaleSizeForGeometry(
{ originalWidth, originalHeight },
{ width, height });
@ -475,7 +496,7 @@ void HistoryVideo::validateGroupedCache(
const auto pixHeight = pixSize.height() * cIntRetinaFactor();
*cacheKey = key;
*cache = image->pixNoCache(_realParent->fullId(), pixWidth, pixHeight, options, width, height);
*cache = (image ? image : Image::Blank().get())->pixNoCache(_realParent->fullId(), pixWidth, pixHeight, options, width, height);
}
void HistoryVideo::setStatusSize(int newSize) const {

View File

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

View File

@ -34,14 +34,16 @@ namespace {
constexpr auto kMaxOriginalEntryLines = 8192;
int articleThumbWidth(PhotoData *thumb, int height) {
auto w = thumb->medium->width();
auto h = thumb->medium->height();
int articleThumbWidth(not_null<PhotoData*> thumb, int height) {
auto w = thumb->thumbnail()->width();
auto h = thumb->thumbnail()->height();
return qMax(qMin(height * w / h, height), 1);
}
int articleThumbHeight(PhotoData *thumb, int width) {
return qMax(thumb->medium->height() * width / thumb->medium->width(), 1);
int articleThumbHeight(not_null<PhotoData*> thumb, int width) {
return qMax(
thumb->thumbnail()->height() * width / thumb->thumbnail()->width(),
1);
}
std::vector<std::unique_ptr<Data::Media>> PrepareCollageMedia(
@ -410,22 +412,25 @@ void HistoryWebPage::draw(Painter &p, const QRect &r, TextSelection selection, T
auto lineHeight = unitedLineHeight();
if (_asArticle) {
const auto contextId = _parent->data()->fullId();
_data->photo->medium->load(contextId, false, false);
bool full = _data->photo->medium->loaded();
_data->photo->loadThumbnail(contextId);
bool full = _data->photo->thumbnail()->loaded();
QPixmap pix;
auto pw = qMax(_pixw, lineHeight);
auto ph = _pixh;
auto pixw = _pixw, pixh = articleThumbHeight(_data->photo, _pixw);
auto maxw = ConvertScale(_data->photo->medium->width()), maxh = ConvertScale(_data->photo->medium->height());
const auto maxw = ConvertScale(_data->photo->thumbnail()->width());
const auto maxh = ConvertScale(_data->photo->thumbnail()->height());
if (pixw * ph != pixh * pw) {
float64 coef = (pixw * ph > pixh * pw) ? qMin(ph / float64(pixh), maxh / float64(pixh)) : qMin(pw / float64(pixw), maxw / float64(pixw));
pixh = qRound(pixh * coef);
pixw = qRound(pixw * coef);
}
if (full) {
pix = _data->photo->medium->pixSingle(contextId, pixw, pixh, pw, ph, ImageRoundRadius::Small);
} else {
pix = _data->photo->thumb->pixBlurredSingle(contextId, pixw, pixh, pw, ph, ImageRoundRadius::Small);
pix = _data->photo->thumbnail()->pixSingle(contextId, pixw, pixh, pw, ph, ImageRoundRadius::Small);
} else if (_data->photo->thumbnailSmall()->loaded()) {
pix = _data->photo->thumbnailSmall()->pixBlurredSingle(contextId, pixw, pixh, pw, ph, ImageRoundRadius::Small);
} else if (const auto blurred = _data->photo->thumbnailInline()) {
pix = blurred->pixBlurredSingle(contextId, pixw, pixh, pw, ph, ImageRoundRadius::Small);
}
p.drawPixmapLeft(padding.left() + paintw - pw, tshift, width(), pix);
if (selected) {

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -198,7 +198,7 @@ SendMediaReady PreparePeerPhoto(PeerId peerId, QImage &&image) {
MTP_long(0)),
MTP_int(image.width()),
MTP_int(image.height()), MTP_int(0)));
photoThumbs.insert(type[0], std::move(image));
photoThumbs.emplace(type[0], std::move(image));
};
push("a", scaled(160));
push("b", scaled(320));
@ -795,15 +795,15 @@ void FileLoadTask::process() {
attributes.push_back(MTP_documentAttributeAnimated());
} else if (_type != SendMediaType::File) {
auto thumb = (w > 100 || h > 100) ? fullimage.scaled(100, 100, Qt::KeepAspectRatio, Qt::SmoothTransformation) : fullimage;
photoThumbs.insert('s', thumb);
photoThumbs.emplace('s', thumb);
photoSizes.push_back(MTP_photoSize(MTP_string("s"), MTP_fileLocationUnavailable(MTP_long(0), MTP_int(0), MTP_long(0)), MTP_int(thumb.width()), MTP_int(thumb.height()), MTP_int(0)));
auto medium = (w > 320 || h > 320) ? fullimage.scaled(320, 320, Qt::KeepAspectRatio, Qt::SmoothTransformation) : fullimage;
photoThumbs.insert('m', medium);
photoThumbs.emplace('m', medium);
photoSizes.push_back(MTP_photoSize(MTP_string("m"), MTP_fileLocationUnavailable(MTP_long(0), MTP_int(0), MTP_long(0)), MTP_int(medium.width()), MTP_int(medium.height()), MTP_int(0)));
auto full = (w > 1280 || h > 1280) ? fullimage.scaled(1280, 1280, Qt::KeepAspectRatio, Qt::SmoothTransformation) : fullimage;
photoThumbs.insert('y', full);
photoThumbs.emplace('y', full);
photoSizes.push_back(MTP_photoSize(MTP_string("y"), MTP_fileLocationUnavailable(MTP_long(0), MTP_int(0), MTP_long(0)), MTP_int(full.width()), MTP_int(full.height()), MTP_int(0)));
{

View File

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

View File

@ -49,7 +49,11 @@ void Document::writeToStream(QDataStream &stream, DocumentData *document) {
writeStorageImageLocation(stream, document->sticker()->loc);
} else {
stream << qint32(document->duration());
writeStorageImageLocation(stream, document->thumb->location());
if (const auto thumb = document->thumbnail()) {
writeStorageImageLocation(stream, thumb->location());
} else {
writeStorageImageLocation(stream, StorageImageLocation());
}
}
}
@ -137,6 +141,7 @@ DocumentData *Document::readFromStreamHelper(int streamAppVersion, QDataStream &
date,
attributes,
mime,
ImagePtr(),
thumb.isNull() ? ImagePtr() : Images::Create(thumb),
dc,
size,
@ -172,7 +177,11 @@ int Document::sizeInStream(DocumentData *document) {
// + duration
result += sizeof(qint32);
// + thumb loc
result += Serialize::storageImageLocationSize(document->thumb->location());
if (const auto thumb = document->thumbnail()) {
result += Serialize::storageImageLocationSize(thumb->location());
} else {
result += Serialize::storageImageLocationSize(StorageImageLocation());
}
}
return result;

View File

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

View File

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

View File

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

View File

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

View File

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