Move inline thumbnail image to DocumentMedia.

This commit is contained in:
John Preston 2020-04-08 19:09:29 +04:00
parent 6ca43153bb
commit bdd3c51ab8
32 changed files with 432 additions and 283 deletions

View File

@ -396,6 +396,8 @@ PRIVATE
data/data_poll.h data/data_poll.h
data/data_pts_waiter.cpp data/data_pts_waiter.cpp
data/data_pts_waiter.h data/data_pts_waiter.h
data/data_reply_preview.cpp
data/data_reply_preview.h
data/data_search_controller.cpp data/data_search_controller.cpp
data/data_search_controller.h data/data_search_controller.h
data/data_session.cpp data/data_session.cpp

View File

@ -442,7 +442,7 @@ void GifsListWidget::processPanelHideFinished() {
if (const auto result = item->getResult()) { if (const auto result = item->getResult()) {
result->unload(); result->unload();
} }
item->unloadAnimation(); item->unloadHeavyPart();
}; };
// Preserve panel state through visibility toggles. // Preserve panel state through visibility toggles.
//clearInlineRows(false); //clearInlineRows(false);

View File

@ -11,6 +11,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "data/data_streaming.h" #include "data/data_streaming.h"
#include "data/data_document_good_thumbnail.h" #include "data/data_document_good_thumbnail.h"
#include "data/data_document_media.h" #include "data/data_document_media.h"
#include "data/data_reply_preview.h"
#include "lang/lang_keys.h" #include "lang/lang_keys.h"
#include "inline_bots/inline_bot_layout_item.h" #include "inline_bots/inline_bot_layout_item.h"
#include "main/main_session.h" #include "main/main_session.h"
@ -470,6 +471,12 @@ DocumentData::DocumentData(not_null<Data::Session*> owner, DocumentId id)
, _owner(owner) { , _owner(owner) {
} }
DocumentData::~DocumentData() {
destroyLoader();
unload();
ActiveCache().remove(this);
}
Data::Session &DocumentData::owner() const { Data::Session &DocumentData::owner() const {
return *_owner; return *_owner;
} }
@ -616,10 +623,11 @@ bool DocumentData::checkWallPaperProperties() {
} }
void DocumentData::updateThumbnails( void DocumentData::updateThumbnails(
ImagePtr thumbnailInline, const QByteArray &inlineThumbnailBytes,
ImagePtr thumbnail) { ImagePtr thumbnail) {
if (thumbnailInline && !_thumbnailInline) { if (!inlineThumbnailBytes.isEmpty()
_thumbnailInline = thumbnailInline; && _inlineThumbnailBytes.isEmpty()) {
_inlineThumbnailBytes = inlineThumbnailBytes;
} }
if (thumbnail if (thumbnail
&& (!_thumbnail && (!_thumbnail
@ -642,10 +650,6 @@ bool DocumentData::hasThumbnail() const {
return !_thumbnail->isNull(); return !_thumbnail->isNull();
} }
Image *DocumentData::thumbnailInline() const {
return _thumbnailInline ? _thumbnailInline.get() : nullptr;
}
Image *DocumentData::thumbnail() const { Image *DocumentData::thumbnail() const {
return _thumbnail ? _thumbnail.get() : nullptr; return _thumbnail ? _thumbnail.get() : nullptr;
} }
@ -750,7 +754,6 @@ void DocumentData::unload() {
// Also, you can't unload() images that you don't own // Also, you can't unload() images that you don't own
// from the destructor, because they're already destroyed. // from the destructor, because they're already destroyed.
// //
//_thumbnailInline->unload();
//_thumbnail->unload(); //_thumbnail->unload();
if (sticker()) { if (sticker()) {
if (sticker()->image) { if (sticker()->image) {
@ -758,7 +761,7 @@ void DocumentData::unload() {
sticker()->image = nullptr; sticker()->image = nullptr;
} }
} }
_replyPreview.clear(); _replyPreview = nullptr;
if (!_data.isEmpty()) { if (!_data.isEmpty()) {
ActiveCache().decrement(_data.size()); ActiveCache().decrement(_data.size());
_data.clear(); _data.clear();
@ -1192,28 +1195,10 @@ bool DocumentData::isStickerSetInstalled() const {
Image *DocumentData::getReplyPreview(Data::FileOrigin origin) { Image *DocumentData::getReplyPreview(Data::FileOrigin origin) {
if (!_thumbnail) { if (!_thumbnail) {
return nullptr; return nullptr;
} else if (_replyPreview } else if (!_replyPreview) {
&& (_replyPreview.good() || !_thumbnail->loaded())) { _replyPreview = std::make_unique<Data::ReplyPreview>(this);
return _replyPreview.image();
} }
const auto option = isVideoMessage() return _replyPreview->image(origin);
? 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.image();
} }
StickerData *DocumentData::sticker() const { StickerData *DocumentData::sticker() const {
@ -1653,12 +1638,6 @@ void DocumentData::collectLocalData(not_null<DocumentData*> local) {
} }
} }
DocumentData::~DocumentData() {
destroyLoader();
unload();
ActiveCache().remove(this);
}
QString DocumentData::ComposeNameString( QString DocumentData::ComposeNameString(
const QString &filename, const QString &filename,
const QString &songTitle, const QString &songTitle,

View File

@ -33,6 +33,7 @@ class Loader;
namespace Data { namespace Data {
class Session; class Session;
class DocumentMedia; class DocumentMedia;
class ReplyPreview;
} // namespace Data } // namespace Data
namespace Main { namespace Main {
@ -84,9 +85,10 @@ namespace Serialize {
class Document; class Document;
} // namespace Serialize; } // namespace Serialize;
class DocumentData { class DocumentData final {
public: public:
DocumentData(not_null<Data::Session*> owner, DocumentId id); DocumentData(not_null<Data::Session*> owner, DocumentId id);
~DocumentData();
[[nodiscard]] Data::Session &owner() const; [[nodiscard]] Data::Session &owner() const;
[[nodiscard]] Main::Session &session() const; [[nodiscard]] Main::Session &session() const;
@ -175,12 +177,15 @@ public:
[[nodiscard]] bool hasThumbnail() const; [[nodiscard]] bool hasThumbnail() const;
void loadThumbnail(Data::FileOrigin origin); void loadThumbnail(Data::FileOrigin origin);
[[nodiscard]] Image *thumbnailInline() const;
[[nodiscard]] Image *thumbnail() const; [[nodiscard]] Image *thumbnail() const;
void updateThumbnails( void updateThumbnails(
ImagePtr thumbnailInline, const QByteArray &inlineThumbnailBytes,
ImagePtr thumbnail); ImagePtr thumbnail);
[[nodiscard]] QByteArray inlineThumbnailBytes() const {
return _inlineThumbnailBytes;
}
[[nodiscard]] Storage::Cache::Key goodThumbnailCacheKey() const; [[nodiscard]] Storage::Cache::Key goodThumbnailCacheKey() const;
[[nodiscard]] bool goodThumbnailChecked() const; [[nodiscard]] bool goodThumbnailChecked() const;
[[nodiscard]] bool goodThumbnailGenerating() const; [[nodiscard]] bool goodThumbnailGenerating() const;
@ -242,8 +247,6 @@ public:
void setInappPlaybackFailed(); void setInappPlaybackFailed();
[[nodiscard]] bool inappPlaybackFailed() const; [[nodiscard]] bool inappPlaybackFailed() const;
~DocumentData();
DocumentId id = 0; DocumentId id = 0;
DocumentType type = FileDocument; DocumentType type = FileDocument;
QSize dimensions; QSize dimensions;
@ -310,9 +313,9 @@ private:
QString _mimeString; QString _mimeString;
WebFileLocation _urlLocation; WebFileLocation _urlLocation;
ImagePtr _thumbnailInline; QByteArray _inlineThumbnailBytes;
ImagePtr _thumbnail; ImagePtr _thumbnail;
Data::ReplyPreview _replyPreview; std::unique_ptr<Data::ReplyPreview> _replyPreview;
std::weak_ptr<Data::DocumentMedia> _media; std::weak_ptr<Data::DocumentMedia> _media;
PhotoData *_goodThumbnailPhoto = nullptr; PhotoData *_goodThumbnailPhoto = nullptr;

View File

@ -108,6 +108,19 @@ void DocumentMedia::setGoodThumbnail(QImage thumbnail) {
_owner->session().downloaderTaskFinished().notify(); _owner->session().downloaderTaskFinished().notify();
} }
Image *DocumentMedia::thumbnailInline() const {
if (!_inlineThumbnail) {
auto image = Images::FromInlineBytes(_owner->inlineThumbnailBytes());
if (!image.isNull()) {
_inlineThumbnail = std::make_unique<Image>(
std::make_unique<Images::ImageSource>(
std::move(image),
"PNG"));
}
}
return _inlineThumbnail.get();
}
void DocumentMedia::GenerateGoodThumbnail(not_null<DocumentData*> document) { void DocumentMedia::GenerateGoodThumbnail(not_null<DocumentData*> document) {
const auto data = document->data(); const auto data = document->data();
const auto type = document->isWallPaper() const auto type = document->isWallPaper()

View File

@ -20,8 +20,9 @@ public:
[[nodiscard]] Image *goodThumbnail() const; [[nodiscard]] Image *goodThumbnail() const;
void setGoodThumbnail(QImage thumbnail); void setGoodThumbnail(QImage thumbnail);
[[nodiscard]] Image *thumbnailInline() const;
// For DocumentData. // For DocumentData.
void validateGoodThumbnail();
static void CheckGoodThumbnail(not_null<DocumentData*> document); static void CheckGoodThumbnail(not_null<DocumentData*> document);
private: private:
@ -36,6 +37,7 @@ private:
const not_null<DocumentData*> _owner; const not_null<DocumentData*> _owner;
std::unique_ptr<Image> _goodThumbnail; std::unique_ptr<Image> _goodThumbnail;
mutable std::unique_ptr<Image> _inlineThumbnail;
Flags _flags; Flags _flags;
}; };

View File

@ -9,6 +9,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "data/data_session.h" #include "data/data_session.h"
#include "data/data_file_origin.h" #include "data/data_file_origin.h"
#include "data/data_reply_preview.h"
#include "ui/image/image.h" #include "ui/image/image.h"
#include "ui/image/image_source.h" #include "ui/image/image_source.h"
#include "main/main_session.h" #include "main/main_session.h"
@ -22,6 +23,8 @@ PhotoData::PhotoData(not_null<Data::Session*> owner, PhotoId id)
, _owner(owner) { , _owner(owner) {
} }
PhotoData::~PhotoData() = default;
Data::Session &PhotoData::owner() const { Data::Session &PhotoData::owner() const {
return *_owner; return *_owner;
} }
@ -105,37 +108,14 @@ void PhotoData::unload() {
_thumbnailSmall->unload(); _thumbnailSmall->unload();
_thumbnail->unload(); _thumbnail->unload();
_large->unload(); _large->unload();
_replyPreview.clear(); _replyPreview = nullptr;
} }
Image *PhotoData::getReplyPreview(Data::FileOrigin origin) { Image *PhotoData::getReplyPreview(Data::FileOrigin origin) {
if (_replyPreview if (!_replyPreview) {
&& (_replyPreview.good() || !_thumbnailSmall->loaded())) { _replyPreview = std::make_unique<Data::ReplyPreview>(this);
return _replyPreview.image();
} }
if (_thumbnailSmall->isDelayedStorageImage() return _replyPreview->image(origin);
&& !_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.image();
} }
void PhotoData::setRemoteLocation( void PhotoData::setRemoteLocation(

View File

@ -15,11 +15,13 @@ class Session;
namespace Data { namespace Data {
class Session; class Session;
class ReplyPreview;
} // namespace Data } // namespace Data
class PhotoData { class PhotoData final {
public: public:
PhotoData(not_null<Data::Session*> owner, PhotoId id); PhotoData(not_null<Data::Session*> owner, PhotoId id);
~PhotoData();
[[nodiscard]] Data::Session &owner() const; [[nodiscard]] Data::Session &owner() const;
[[nodiscard]] Main::Session &session() const; [[nodiscard]] Main::Session &session() const;
@ -97,7 +99,7 @@ private:
int32 _dc = 0; int32 _dc = 0;
uint64 _access = 0; uint64 _access = 0;
QByteArray _fileReference; QByteArray _fileReference;
Data::ReplyPreview _replyPreview; std::unique_ptr<Data::ReplyPreview> _replyPreview;
not_null<Data::Session*> _owner; not_null<Data::Session*> _owner;

View File

@ -0,0 +1,101 @@
/*
This file is part of Telegram Desktop,
the official desktop application for the Telegram messaging service.
For license and copyright information please follow this link:
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#include "data/data_reply_preview.h"
#include "data/data_file_origin.h"
#include "data/data_document.h"
#include "data/data_document_media.h"
#include "data/data_photo.h"
#include "ui/image/image.h"
#include "ui/image/image_source.h"
namespace Data {
ReplyPreview::ReplyPreview(not_null<DocumentData*> document)
: _document(document)
, _documentMedia(_document->createMediaView()) {
}
ReplyPreview::ReplyPreview(not_null<PhotoData*> photo)
: _photo(photo) {
}
void ReplyPreview::prepare(not_null<Image*> image, Images::Options options) {
if (image->isNull() || !image->loaded()) {
return;
}
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(
FileOrigin(),
thumbSize.width(),
thumbSize.height(),
prepareOptions,
outerSize,
outerSize);
_image = std::make_unique<Image>(
std::make_unique<Images::ImageSource>(
bitmap.toImage(),
"PNG"));
_good = ((options & Images::Option::Blurred) == 0);
}
Image *ReplyPreview::image(Data::FileOrigin origin) {
if (_document) {
const auto thumbnail = _document->thumbnail();
Assert(thumbnail != nullptr);
if (!_image || (!_good && thumbnail->loaded())) {
const auto option = _document->isVideoMessage()
? Images::Option::Circled
: Images::Option::None;
if (thumbnail->loaded()) {
prepare(thumbnail, option);
} else {
thumbnail->load(origin);
if (const auto image = _documentMedia->thumbnailInline()) {
prepare(image, option | Images::Option::Blurred);
}
}
}
} else {
Assert(_photo != nullptr);
const auto small = _photo->thumbnailSmall();
const auto large = _photo->large();
if (!_image || (!_good && (small->loaded() || large->loaded()))) {
if (small->isDelayedStorageImage()
&& !large->isNull()
&& !large->isDelayedStorageImage()
&& large->loaded()) {
prepare(large, Images::Option(0));
} else if (small->loaded()) {
prepare(small, Images::Option(0));
} else {
small->load(origin);
if (const auto blurred = _photo->thumbnailInline()) {
prepare(blurred, Images::Option::Blurred);
}
}
}
}
return _image.get();
}
} // namespace Data

View File

@ -0,0 +1,36 @@
/*
This file is part of Telegram Desktop,
the official desktop application for the Telegram messaging service.
For license and copyright information please follow this link:
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#pragma once
class DocumentData;
class PhotoData;
namespace Data {
class DocumentMedia;
struct FileOrigin;
class ReplyPreview {
public:
explicit ReplyPreview(not_null<DocumentData*> document);
explicit ReplyPreview(not_null<PhotoData*> photo);
[[nodiscard]] Image *image(Data::FileOrigin origin);
private:
void prepare(not_null<Image*> image, Images::Options options);
std::unique_ptr<Image> _image;
bool _good = false;
DocumentData *_document = nullptr;
PhotoData *_photo = nullptr;
std::shared_ptr<DocumentMedia> _documentMedia;
};
} // namespace Data

View File

@ -126,19 +126,20 @@ std::vector<UnavailableReason> ExtractUnavailableReasons(
}) | ranges::to_vector; }) | ranges::to_vector;
} }
MTPPhotoSize FindDocumentInlineThumbnail(const MTPDdocument &data) { QByteArray FindDocumentInlineThumbnail(const MTPDdocument &data) {
const auto thumbs = data.vthumbs(); const auto thumbs = data.vthumbs();
if (!thumbs) { if (!thumbs) {
return MTP_photoSizeEmpty(MTP_string()); return QByteArray();
} }
const auto &list = thumbs->v; const auto &list = thumbs->v;
const auto i = ranges::find( const auto i = ranges::find(
list, list,
mtpc_photoStrippedSize, mtpc_photoStrippedSize,
&MTPPhotoSize::type); &MTPPhotoSize::type);
return (i != list.end()) if (i == list.end()) {
? (*i) return QByteArray();
: MTPPhotoSize(MTP_photoSizeEmpty(MTP_string())); }
return i->c_photoStrippedSize().vbytes().v;
} }
MTPPhotoSize FindDocumentThumbnail(const MTPDdocument &data) { MTPPhotoSize FindDocumentThumbnail(const MTPDdocument &data) {
@ -2371,7 +2372,7 @@ not_null<DocumentData*> Session::processDocument(
fields.vdate().v, fields.vdate().v,
fields.vattributes().v, fields.vattributes().v,
mime, mime,
ImagePtr(), QByteArray(),
Images::Create(std::move(thumb), format), Images::Create(std::move(thumb), format),
fields.vdc_id().v, fields.vdc_id().v,
fields.vsize().v, fields.vsize().v,
@ -2388,7 +2389,7 @@ not_null<DocumentData*> Session::document(
TimeId date, TimeId date,
const QVector<MTPDocumentAttribute> &attributes, const QVector<MTPDocumentAttribute> &attributes,
const QString &mime, const QString &mime,
const ImagePtr &thumbnailInline, const QByteArray &inlineThumbnailBytes,
const ImagePtr &thumbnail, const ImagePtr &thumbnail,
int32 dc, int32 dc,
int32 size, int32 size,
@ -2401,7 +2402,7 @@ not_null<DocumentData*> Session::document(
date, date,
attributes, attributes,
mime, mime,
thumbnailInline, inlineThumbnailBytes,
thumbnail, thumbnail,
dc, dc,
size, size,
@ -2476,7 +2477,7 @@ DocumentData *Session::documentFromWeb(
base::unixtime::now(), base::unixtime::now(),
data.vattributes().v, data.vattributes().v,
data.vmime_type().v, data.vmime_type().v,
ImagePtr(), QByteArray(),
thumb, thumb,
MTP::maindc(), MTP::maindc(),
int32(0), // data.vsize().v int32(0), // data.vsize().v
@ -2497,7 +2498,7 @@ DocumentData *Session::documentFromWeb(
base::unixtime::now(), base::unixtime::now(),
data.vattributes().v, data.vattributes().v,
data.vmime_type().v, data.vmime_type().v,
ImagePtr(), QByteArray(),
thumb, thumb,
MTP::maindc(), MTP::maindc(),
int32(0), // data.vsize().v int32(0), // data.vsize().v
@ -2517,7 +2518,7 @@ void Session::documentApplyFields(
void Session::documentApplyFields( void Session::documentApplyFields(
not_null<DocumentData*> document, not_null<DocumentData*> document,
const MTPDdocument &data) { const MTPDdocument &data) {
const auto thumbnailInline = FindDocumentInlineThumbnail(data); const auto inlineThumbnailBytes = FindDocumentInlineThumbnail(data);
const auto thumbnailSize = FindDocumentThumbnail(data); const auto thumbnailSize = FindDocumentThumbnail(data);
const auto thumbnail = Images::Create(data, thumbnailSize); const auto thumbnail = Images::Create(data, thumbnailSize);
documentApplyFields( documentApplyFields(
@ -2527,7 +2528,7 @@ void Session::documentApplyFields(
data.vdate().v, data.vdate().v,
data.vattributes().v, data.vattributes().v,
qs(data.vmime_type()), qs(data.vmime_type()),
Images::Create(data, thumbnailInline), inlineThumbnailBytes,
thumbnail, thumbnail,
data.vdc_id().v, data.vdc_id().v,
data.vsize().v, data.vsize().v,
@ -2541,7 +2542,7 @@ void Session::documentApplyFields(
TimeId date, TimeId date,
const QVector<MTPDocumentAttribute> &attributes, const QVector<MTPDocumentAttribute> &attributes,
const QString &mime, const QString &mime,
const ImagePtr &thumbnailInline, const QByteArray &inlineThumbnailBytes,
const ImagePtr &thumbnail, const ImagePtr &thumbnail,
int32 dc, int32 dc,
int32 size, int32 size,
@ -2551,7 +2552,7 @@ void Session::documentApplyFields(
} }
document->date = date; document->date = date;
document->setMimeString(mime); document->setMimeString(mime);
document->updateThumbnails(thumbnailInline, thumbnail); document->updateThumbnails(inlineThumbnailBytes, thumbnail);
document->size = size; document->size = size;
document->setattributes(attributes); document->setattributes(attributes);

View File

@ -494,7 +494,7 @@ public:
TimeId date, TimeId date,
const QVector<MTPDocumentAttribute> &attributes, const QVector<MTPDocumentAttribute> &attributes,
const QString &mime, const QString &mime,
const ImagePtr &thumbnailInline, const QByteArray &inlineThumbnailBytes,
const ImagePtr &thumbnail, const ImagePtr &thumbnail,
int32 dc, int32 dc,
int32 size, int32 size,
@ -749,7 +749,7 @@ private:
TimeId date, TimeId date,
const QVector<MTPDocumentAttribute> &attributes, const QVector<MTPDocumentAttribute> &attributes,
const QString &mime, const QString &mime,
const ImagePtr &thumbnailInline, const QByteArray &inlineThumbnailBytes,
const ImagePtr &thumbnail, const ImagePtr &thumbnail,
int32 dc, int32 dc,
int32 size, int32 size,

View File

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

View File

@ -56,33 +56,6 @@ constexpr auto kAnimationCacheTag = uint8(0x05);
struct FileOrigin; struct FileOrigin;
class ReplyPreview {
public:
ReplyPreview();
ReplyPreview(ReplyPreview &&other);
ReplyPreview &operator=(ReplyPreview &&other);
~ReplyPreview();
void prepare(
not_null<Image*> image,
FileOrigin origin,
Images::Options options);
void clear();
[[nodiscard]] Image *image() const;
[[nodiscard]] bool good() const;
[[nodiscard]] bool empty() const;
[[nodiscard]] explicit operator bool() const {
return !empty();
}
private:
struct Data;
std::unique_ptr<Data> _data;
};
} // namespace Data } // namespace Data
struct MessageGroupId { struct MessageGroupId {

View File

@ -20,6 +20,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "ui/image/image.h" #include "ui/image/image.h"
#include "data/data_session.h" #include "data/data_session.h"
#include "data/data_document.h" #include "data/data_document.h"
#include "data/data_document_media.h"
#include "data/data_media_types.h" #include "data/data_media_types.h"
#include "data/data_file_origin.h" #include "data/data_file_origin.h"
#include "app.h" #include "app.h"
@ -262,6 +263,8 @@ void Document::draw(Painter &p, const QRect &r, TextSelection selection, crl::ti
auto topMinus = isBubbleTop() ? 0 : st::msgFileTopMinus; auto topMinus = isBubbleTop() ? 0 : st::msgFileTopMinus;
int nameleft = 0, nametop = 0, nameright = 0, statustop = 0, linktop = 0, bottom = 0; int nameleft = 0, nametop = 0, nameright = 0, statustop = 0, linktop = 0, bottom = 0;
if (auto thumbed = Get<HistoryDocumentThumbed>()) { if (auto thumbed = Get<HistoryDocumentThumbed>()) {
ensureDataMediaCreated();
nameleft = st::msgFileThumbPadding.left() + st::msgFileThumbSize + st::msgFileThumbPadding.right(); nameleft = st::msgFileThumbPadding.left() + st::msgFileThumbSize + st::msgFileThumbPadding.right();
nametop = st::msgFileThumbNameTop - topMinus; nametop = st::msgFileThumbNameTop - topMinus;
nameright = st::msgFileThumbPadding.left(); nameright = st::msgFileThumbPadding.left();
@ -278,7 +281,7 @@ void Document::draw(Painter &p, const QRect &r, TextSelection selection, crl::ti
thumb = normal->pixSingle(_realParent->fullId(), thumbed->_thumbw, 0, st::msgFileThumbSize, st::msgFileThumbSize, roundRadius); thumb = normal->pixSingle(_realParent->fullId(), thumbed->_thumbw, 0, st::msgFileThumbSize, st::msgFileThumbSize, roundRadius);
} else { } else {
_data->loadThumbnail(_realParent->fullId()); _data->loadThumbnail(_realParent->fullId());
if (const auto blurred = _data->thumbnailInline()) { if (const auto blurred = _dataMedia->thumbnailInline()) {
thumb = blurred->pixBlurredSingle(_realParent->fullId(), thumbed->_thumbw, 0, st::msgFileThumbSize, st::msgFileThumbSize, roundRadius); thumb = blurred->pixBlurredSingle(_realParent->fullId(), thumbed->_thumbw, 0, st::msgFileThumbSize, st::msgFileThumbSize, roundRadius);
} }
} }
@ -491,6 +494,24 @@ void Document::draw(Painter &p, const QRect &r, TextSelection selection, crl::ti
} }
} }
void Document::checkHeavyPart() {
if (!_dataMedia) {
history()->owner().unregisterHeavyViewPart(_parent);
}
}
void Document::unloadHeavyPart() {
_dataMedia = nullptr;
}
void Document::ensureDataMediaCreated() const {
if (_dataMedia) {
return;
}
_dataMedia = _data->createMediaView();
history()->owner().registerHeavyViewPart(_parent);
}
bool Document::downloadInCorner() const { bool Document::downloadInCorner() const {
return _data->isAudioFile() return _data->isAudioFile()
&& _data->canBeStreamed() && _data->canBeStreamed()

View File

@ -12,6 +12,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
struct HistoryDocumentNamed; struct HistoryDocumentNamed;
namespace Data {
class DocumentMedia;
} // namespace Data
namespace Ui { namespace Ui {
namespace Text { namespace Text {
class String; class String;
@ -63,6 +67,9 @@ public:
void refreshParentId(not_null<HistoryItem*> realParent) override; void refreshParentId(not_null<HistoryItem*> realParent) override;
void parentTextUpdated() override; void parentTextUpdated() override;
void checkHeavyPart() override;
void unloadHeavyPart() override;
protected: protected:
float64 dataProgress() const override; float64 dataProgress() const override;
bool dataFinished() const override; bool dataFinished() const override;
@ -75,6 +82,8 @@ private:
int realDuration = 0; int realDuration = 0;
}; };
void ensureDataMediaCreated() const;
[[nodiscard]] Ui::Text::String createCaption(); [[nodiscard]] Ui::Text::String createCaption();
QSize countOptimalSize() override; QSize countOptimalSize() override;
@ -93,6 +102,7 @@ private:
StateRequest request) const; StateRequest request) const;
not_null<DocumentData*> _data; not_null<DocumentData*> _data;
mutable std::shared_ptr<Data::DocumentMedia> _dataMedia;
}; };

View File

@ -425,7 +425,7 @@ void Gif::draw(Painter &p, const QRect &r, TextSelection selection, crl::time ms
} }
} else { } else {
_data->loadThumbnail(_realParent->fullId()); _data->loadThumbnail(_realParent->fullId());
if (const auto blurred = _data->thumbnailInline()) { if (const auto blurred = _dataMedia->thumbnailInline()) {
p.drawPixmap(rthumb.topLeft(), blurred->pixBlurredSingle(_realParent->fullId(), _thumbw, _thumbh, usew, painth, roundRadius, roundCorners)); p.drawPixmap(rthumb.topLeft(), blurred->pixBlurredSingle(_realParent->fullId(), _thumbw, _thumbh, usew, painth, roundRadius, roundCorners));
} else if (!isRound) { } else if (!isRound) {
const auto roundTop = (roundCorners & RectPart::TopLeft); const auto roundTop = (roundCorners & RectPart::TopLeft);
@ -1140,7 +1140,7 @@ void Gif::validateGroupedCache(
? good ? good
: useThumb : useThumb
? thumb ? thumb
: _data->thumbnailInline(); : _dataMedia->thumbnailInline();
const auto blur = !useGood const auto blur = !useGood
&& (!useThumb && (!useThumb
|| (thumb->width() < kUseNonBlurredThreshold || (thumb->width() < kUseNonBlurredThreshold

View File

@ -207,8 +207,8 @@ void ThemeDocument::validateThumbnail() const {
} }
if (_data->thumbnail()->loaded()) { if (_data->thumbnail()->loaded()) {
prepareThumbnailFrom(_data->thumbnail(), 0); prepareThumbnailFrom(_data->thumbnail(), 0);
} else if (const auto blurred = _data->thumbnailInline()) { } else if (_thumbnail.isNull()) {
if (_thumbnail.isNull()) { if (const auto blurred = _dataMedia->thumbnailInline()) {
prepareThumbnailFrom(blurred, -1); prepareThumbnailFrom(blurred, -1);
} }
} }

View File

@ -11,6 +11,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "data/data_document.h" #include "data/data_document.h"
#include "data/data_session.h" #include "data/data_session.h"
#include "data/data_file_origin.h" #include "data/data_file_origin.h"
#include "data/data_document_media.h"
#include "styles/style_overview.h" #include "styles/style_overview.h"
#include "styles/style_history.h" #include "styles/style_history.h"
#include "styles/style_chat_helpers.h" #include "styles/style_chat_helpers.h"
@ -325,13 +326,21 @@ void Gif::validateThumbnail(
void Gif::prepareThumbnail(QSize size, QSize frame) const { void Gif::prepareThumbnail(QSize size, QSize frame) const {
if (const auto document = getShownDocument()) { if (const auto document = getShownDocument()) {
ensureDataMediaCreated(document);
validateThumbnail(document->thumbnail(), size, frame, true); validateThumbnail(document->thumbnail(), size, frame, true);
validateThumbnail(document->thumbnailInline(), size, frame, false); validateThumbnail(_dataMedia->thumbnailInline(), size, frame, false);
} else { } else {
validateThumbnail(getResultThumb(), size, frame, true); validateThumbnail(getResultThumb(), size, frame, true);
} }
} }
void Gif::ensureDataMediaCreated(not_null<DocumentData*> document) const {
if (_dataMedia) {
return;
}
_dataMedia = document->createMediaView();
}
void Gif::ensureAnimation() const { void Gif::ensureAnimation() const {
if (!_animation) { if (!_animation) {
_animation = std::make_unique<AnimationData>([=](crl::time now) { _animation = std::make_unique<AnimationData>([=](crl::time now) {
@ -367,9 +376,14 @@ void Gif::radialAnimationCallback(crl::time now) const {
} }
} }
void Gif::unloadHeavyPart() {
unloadAnimation();
getShownDocument()->unload();
_dataMedia = nullptr;
}
void Gif::unloadAnimation() { void Gif::unloadAnimation() {
_gif.reset(); _gif.reset();
getShownDocument()->unload();
} }
void Gif::clipCallback(Media::Clip::Notification notification) { void Gif::clipCallback(Media::Clip::Notification notification) {
@ -1380,11 +1394,19 @@ void Game::prepareThumbnail(QSize size) const {
validateThumbnail(photo->thumbnail(), size, true); validateThumbnail(photo->thumbnail(), size, true);
validateThumbnail(photo->thumbnailInline(), size, false); validateThumbnail(photo->thumbnailInline(), size, false);
} else if (const auto document = getResultDocument()) { } else if (const auto document = getResultDocument()) {
ensureDataMediaCreated(document);
validateThumbnail(document->thumbnail(), size, true); validateThumbnail(document->thumbnail(), size, true);
validateThumbnail(document->thumbnailInline(), size, false); validateThumbnail(_dataMedia->thumbnailInline(), size, false);
} }
} }
void Game::ensureDataMediaCreated(not_null<DocumentData*> document) const {
if (_dataMedia) {
return;
}
_dataMedia = document->createMediaView();
}
void Game::validateThumbnail(Image *image, QSize size, bool good) const { void Game::validateThumbnail(Image *image, QSize size, bool good) const {
if (!image || (_thumbGood && !good)) { if (!image || (_thumbGood && !good)) {
return; return;
@ -1450,9 +1472,14 @@ void Game::radialAnimationCallback(crl::time now) const {
} }
} }
void Game::unloadHeavyPart() {
unloadAnimation();
getResultDocument()->unload();
_dataMedia = nullptr;
}
void Game::unloadAnimation() { void Game::unloadAnimation() {
_gif.reset(); _gif.reset();
getResultDocument()->unload();
} }
void Game::clipCallback(Media::Clip::Notification notification) { void Game::clipCallback(Media::Clip::Notification notification) {
@ -1464,7 +1491,20 @@ void Game::clipCallback(Media::Clip::Notification notification) {
_gif.setBad(); _gif.setBad();
getResultDocument()->unload(); getResultDocument()->unload();
} else if (_gif->ready() && !_gif->started()) { } else if (_gif->ready() && !_gif->started()) {
_gif->start(_frameSize.width(), _frameSize.height(), st::inlineThumbSize, st::inlineThumbSize, ImageRoundRadius::None, RectPart::None); if (_gif->width() * _gif->height() > kMaxInlineArea) {
getResultDocument()->dimensions = QSize(
_gif->width(),
_gif->height());
unloadAnimation();
} else {
_gif->start(
_frameSize.width(),
_frameSize.height(),
st::inlineThumbSize,
st::inlineThumbSize,
ImageRoundRadius::None,
RectPart::None);
}
} else if (_gif->autoPausedGif() && !context()->inlineItemVisible(this)) { } else if (_gif->autoPausedGif() && !context()->inlineItemVisible(this)) {
unloadAnimation(); unloadAnimation();
} }

View File

@ -18,6 +18,10 @@ namespace Lottie {
class SinglePlayer; class SinglePlayer;
} // namespace Lottie } // namespace Lottie
namespace Data {
class DocumentMedia;
} // namespace Data
namespace InlineBots { namespace InlineBots {
namespace Layout { namespace Layout {
namespace internal { namespace internal {
@ -75,23 +79,30 @@ public:
int resizeGetHeight(int width) override; int resizeGetHeight(int width) override;
void unloadAnimation() override; void unloadHeavyPart() override;
private: private:
QSize countFrameSize() const;
enum class StateFlag { enum class StateFlag {
Over = (1 << 0), Over = (1 << 0),
DeleteOver = (1 << 1), DeleteOver = (1 << 1),
}; };
using StateFlags = base::flags<StateFlag>; using StateFlags = base::flags<StateFlag>;
friend inline constexpr auto is_flag_type(StateFlag) { return true; }; friend inline constexpr auto is_flag_type(StateFlag) { return true; };
StateFlags _state;
Media::Clip::ReaderPointer _gif; struct AnimationData {
ClickHandlerPtr _delete; template <typename Callback>
mutable QPixmap _thumb; AnimationData(Callback &&callback)
mutable bool _thumbGood = false; : radial(std::forward<Callback>(callback)) {
}
bool over = false;
Ui::Animations::Simple _a_over;
Ui::RadialAnimation radial;
};
void ensureDataMediaCreated(not_null<DocumentData*> document) const;
void unloadAnimation();
QSize countFrameSize() const;
void validateThumbnail( void validateThumbnail(
Image *image, Image *image,
QSize size, QSize size,
@ -105,15 +116,15 @@ private:
void clipCallback(Media::Clip::Notification notification); void clipCallback(Media::Clip::Notification notification);
struct AnimationData { StateFlags _state;
template <typename Callback>
AnimationData(Callback &&callback) Media::Clip::ReaderPointer _gif;
: radial(std::forward<Callback>(callback)) { ClickHandlerPtr _delete;
} mutable QPixmap _thumb;
bool over = false; mutable bool _thumbGood = false;
Ui::Animations::Simple _a_over;
Ui::RadialAnimation radial; mutable std::shared_ptr<Data::DocumentMedia> _dataMedia;
};
mutable std::unique_ptr<AnimationData> _animation; mutable std::unique_ptr<AnimationData> _animation;
mutable Ui::Animations::Simple _a_deleteOver; mutable Ui::Animations::Simple _a_deleteOver;
@ -372,9 +383,11 @@ public:
QPoint point, QPoint point,
StateRequest request) const override; StateRequest request) const override;
void unloadAnimation() override; void unloadHeavyPart() override;
private: private:
void ensureDataMediaCreated(not_null<DocumentData*> document) const;
void unloadAnimation();
void countFrameSize(); void countFrameSize();
void prepareThumbnail(QSize size) const; void prepareThumbnail(QSize size) const;
@ -386,6 +399,7 @@ private:
void clipCallback(Media::Clip::Notification notification); void clipCallback(Media::Clip::Notification notification);
Media::Clip::ReaderPointer _gif; Media::Clip::ReaderPointer _gif;
mutable std::shared_ptr<Data::DocumentMedia> _dataMedia;
mutable QPixmap _thumb; mutable QPixmap _thumb;
mutable bool _thumbGood = false; mutable bool _thumbGood = false;
mutable std::unique_ptr<Ui::RadialAnimation> _radial; mutable std::unique_ptr<Ui::RadialAnimation> _radial;

View File

@ -80,7 +80,7 @@ public:
PhotoData *getPreviewPhoto() const; PhotoData *getPreviewPhoto() const;
virtual void preload() const; virtual void preload() const;
virtual void unloadAnimation() { virtual void unloadHeavyPart() {
} }
void update() const; void update() const;

View File

@ -304,7 +304,7 @@ void Inner::hideFinish(bool completely) {
if (const auto result = item->getResult()) { if (const auto result = item->getResult()) {
result->unload(); result->unload();
} }
item->unloadAnimation(); item->unloadHeavyPart();
}; };
clearInlineRows(false); clearInlineRows(false);
for (const auto &[result, layout] : _inlineLayouts) { for (const auto &[result, layout] : _inlineLayouts) {

View File

@ -2183,7 +2183,10 @@ void OverlayWidget::initStreamingThumbnail() {
const auto useGood = (good && good->loaded()); const auto useGood = (good && good->loaded());
const auto thumb = _doc->thumbnail(); const auto thumb = _doc->thumbnail();
const auto useThumb = (thumb && thumb->loaded()); const auto useThumb = (thumb && thumb->loaded());
const auto blurred = _doc->thumbnailInline();
// #TODO optimize
const auto blurred = media ? media->thumbnailInline() : nullptr;
if (good && !useGood) { if (good && !useGood) {
good->load({}); good->load({});
} else if (thumb && !useThumb) { } else if (thumb && !useThumb) {

View File

@ -1376,7 +1376,10 @@ QImage Pip::videoFrame(const FrameRequest &request) const {
const auto useGood = (good && good->loaded()); const auto useGood = (good && good->loaded());
const auto thumb = _data->thumbnail(); const auto thumb = _data->thumbnail();
const auto useThumb = (thumb && thumb->loaded()); const auto useThumb = (thumb && thumb->loaded());
const auto blurred = _data->thumbnailInline();
// #TODO optimize always use when available
const auto blurred = media ? media->thumbnailInline() : nullptr;
const auto state = !cover.isNull() const auto state = !cover.isNull()
? ThumbState::Cover ? ThumbState::Cover
: useGood : useGood

View File

@ -437,7 +437,7 @@ void Video::paint(Painter &p, const QRect &clip, TextSelection selection, const
ensureDataMediaCreated(); ensureDataMediaCreated();
const auto selected = (selection == FullSelection); const auto selected = (selection == FullSelection);
const auto blurred = _data->thumbnailInline(); const auto blurred = _dataMedia->thumbnailInline();
const auto thumbLoaded = _data->hasThumbnail() const auto thumbLoaded = _data->hasThumbnail()
&& _data->thumbnail()->loaded(); && _data->thumbnail()->loaded();
const auto goodLoaded = _dataMedia->goodThumbnail() const auto goodLoaded = _dataMedia->goodThumbnail()
@ -672,10 +672,15 @@ void Voice::paint(Painter &p, const QRect &clip, TextSelection selection, const
_st.songThumbSize, _st.songThumbSize,
_width); _width);
if (clip.intersects(inner)) { if (clip.intersects(inner)) {
if (_data->hasThumbnail()) {
ensureDataMediaCreated();
}
p.setPen(Qt::NoPen); p.setPen(Qt::NoPen);
const auto thumbLoaded = _data->hasThumbnail() const auto thumbLoaded = _data->hasThumbnail()
&& _data->thumbnail()->loaded(); && _data->thumbnail()->loaded();
const auto blurred = _data->thumbnailInline(); const auto blurred = _dataMedia
? _dataMedia->thumbnailInline()
: nullptr;
if (thumbLoaded || blurred) { if (thumbLoaded || blurred) {
const auto thumb = thumbLoaded const auto thumb = thumbLoaded
? _data->thumbnail()->pixCircled( ? _data->thumbnail()->pixCircled(
@ -826,6 +831,13 @@ TextState Voice::getState(
return result; return result;
} }
void Voice::ensureDataMediaCreated() const {
if (_dataMedia) {
return;
}
_dataMedia = _data->createMediaView();
}
float64 Voice::dataProgress() const { float64 Voice::dataProgress() const {
return _data->progress(); return _data->progress();
} }
@ -1023,8 +1035,9 @@ void Document::paint(Painter &p, const QRect &clip, TextSelection selection, con
QRect rthumb(style::rtlrect(0, st::linksBorder + _st.filePadding.top(), _st.fileThumbSize, _st.fileThumbSize, _width)); QRect rthumb(style::rtlrect(0, st::linksBorder + _st.filePadding.top(), _st.fileThumbSize, _st.fileThumbSize, _width));
if (clip.intersects(rthumb)) { if (clip.intersects(rthumb)) {
if (wthumb) { if (wthumb) {
ensureDataMediaCreated();
const auto thumbLoaded = _data->thumbnail()->loaded(); const auto thumbLoaded = _data->thumbnail()->loaded();
const auto blurred = _data->thumbnailInline(); const auto blurred = _dataMedia->thumbnailInline();
if (thumbLoaded || blurred) { if (thumbLoaded || blurred) {
if (_thumb.isNull() || (thumbLoaded && !_thumbLoaded)) { if (_thumb.isNull() || (thumbLoaded && !_thumbLoaded)) {
_thumbLoaded = thumbLoaded; _thumbLoaded = thumbLoaded;
@ -1279,6 +1292,13 @@ const style::RoundCheckbox &Document::checkboxStyle() const {
return st::overviewSmallCheck; return st::overviewSmallCheck;
} }
void Document::ensureDataMediaCreated() const {
if (_dataMedia) {
return;
}
_dataMedia = _data->createMediaView();
}
float64 Document::dataProgress() const { float64 Document::dataProgress() const {
return _data->progress(); return _data->progress();
} }

View File

@ -255,7 +255,7 @@ private:
}; };
class Voice : public RadialProgressItem { class Voice final : public RadialProgressItem {
public: public:
Voice( Voice(
not_null<HistoryItem*> parent, not_null<HistoryItem*> parent,
@ -276,9 +276,11 @@ protected:
const style::RoundCheckbox &checkboxStyle() const override; const style::RoundCheckbox &checkboxStyle() const override;
private: private:
void ensureDataMediaCreated() const;
int duration() const; int duration() const;
not_null<DocumentData*> _data; not_null<DocumentData*> _data;
mutable std::shared_ptr<Data::DocumentMedia> _dataMedia;
StatusText _status; StatusText _status;
ClickHandlerPtr _namel; ClickHandlerPtr _namel;
@ -292,7 +294,7 @@ private:
}; };
class Document : public RadialProgressItem { class Document final : public RadialProgressItem {
public: public:
Document( Document(
not_null<HistoryItem*> parent, not_null<HistoryItem*> parent,
@ -305,7 +307,7 @@ public:
QPoint point, QPoint point,
StateRequest request) const override; StateRequest request) const override;
virtual DocumentData *getDocument() const override { DocumentData *getDocument() const override {
return _data; return _data;
} }
@ -323,7 +325,10 @@ private:
QPoint point, QPoint point,
StateRequest request) const; StateRequest request) const;
void ensureDataMediaCreated() const;
not_null<DocumentData*> _data; not_null<DocumentData*> _data;
mutable std::shared_ptr<Data::DocumentMedia> _dataMedia;
StatusText _status; StatusText _status;
ClickHandlerPtr _msgl, _namel; ClickHandlerPtr _msgl, _namel;
@ -342,7 +347,7 @@ private:
}; };
class Link : public ItemBase { class Link final : public ItemBase {
public: public:
Link( Link(
not_null<HistoryItem*> parent, not_null<HistoryItem*> parent,

View File

@ -3942,7 +3942,7 @@ void importOldRecentStickers() {
date, date,
attributes, attributes,
mime, mime,
ImagePtr(), QByteArray(),
ImagePtr(), ImagePtr(),
dc, dc,
size, size,

View File

@ -146,7 +146,7 @@ DocumentData *Document::readFromStreamHelper(int streamAppVersion, QDataStream &
date, date,
attributes, attributes,
mime, mime,
ImagePtr(), QByteArray(),
Images::Create(*thumb), Images::Create(*thumb),
dc, dc,
size, size,

View File

@ -61,6 +61,56 @@ uint64 SinglePixKey(Options options) {
} // namespace } // namespace
QImage FromInlineBytes(const QByteArray &bytes) {
if (bytes.size() < 3 || bytes[0] != '\x01') {
return QImage();
}
const char header[] = "\xff\xd8\xff\xe0\x00\x10\x4a\x46\x49"
"\x46\x00\x01\x01\x00\x00\x01\x00\x01\x00\x00\xff\xdb\x00\x43\x00\x28\x1c"
"\x1e\x23\x1e\x19\x28\x23\x21\x23\x2d\x2b\x28\x30\x3c\x64\x41\x3c\x37\x37"
"\x3c\x7b\x58\x5d\x49\x64\x91\x80\x99\x96\x8f\x80\x8c\x8a\xa0\xb4\xe6\xc3"
"\xa0\xaa\xda\xad\x8a\x8c\xc8\xff\xcb\xda\xee\xf5\xff\xff\xff\x9b\xc1\xff"
"\xff\xff\xfa\xff\xe6\xfd\xff\xf8\xff\xdb\x00\x43\x01\x2b\x2d\x2d\x3c\x35"
"\x3c\x76\x41\x41\x76\xf8\xa5\x8c\xa5\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8"
"\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8"
"\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8"
"\xf8\xf8\xf8\xf8\xf8\xff\xc0\x00\x11\x08\x00\x00\x00\x00\x03\x01\x22\x00"
"\x02\x11\x01\x03\x11\x01\xff\xc4\x00\x1f\x00\x00\x01\x05\x01\x01\x01\x01"
"\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x01\x02\x03\x04\x05\x06\x07\x08"
"\x09\x0a\x0b\xff\xc4\x00\xb5\x10\x00\x02\x01\x03\x03\x02\x04\x03\x05\x05"
"\x04\x04\x00\x00\x01\x7d\x01\x02\x03\x00\x04\x11\x05\x12\x21\x31\x41\x06"
"\x13\x51\x61\x07\x22\x71\x14\x32\x81\x91\xa1\x08\x23\x42\xb1\xc1\x15\x52"
"\xd1\xf0\x24\x33\x62\x72\x82\x09\x0a\x16\x17\x18\x19\x1a\x25\x26\x27\x28"
"\x29\x2a\x34\x35\x36\x37\x38\x39\x3a\x43\x44\x45\x46\x47\x48\x49\x4a\x53"
"\x54\x55\x56\x57\x58\x59\x5a\x63\x64\x65\x66\x67\x68\x69\x6a\x73\x74\x75"
"\x76\x77\x78\x79\x7a\x83\x84\x85\x86\x87\x88\x89\x8a\x92\x93\x94\x95\x96"
"\x97\x98\x99\x9a\xa2\xa3\xa4\xa5\xa6\xa7\xa8\xa9\xaa\xb2\xb3\xb4\xb5\xb6"
"\xb7\xb8\xb9\xba\xc2\xc3\xc4\xc5\xc6\xc7\xc8\xc9\xca\xd2\xd3\xd4\xd5\xd6"
"\xd7\xd8\xd9\xda\xe1\xe2\xe3\xe4\xe5\xe6\xe7\xe8\xe9\xea\xf1\xf2\xf3\xf4"
"\xf5\xf6\xf7\xf8\xf9\xfa\xff\xc4\x00\x1f\x01\x00\x03\x01\x01\x01\x01\x01"
"\x01\x01\x01\x01\x00\x00\x00\x00\x00\x00\x01\x02\x03\x04\x05\x06\x07\x08"
"\x09\x0a\x0b\xff\xc4\x00\xb5\x11\x00\x02\x01\x02\x04\x04\x03\x04\x07\x05"
"\x04\x04\x00\x01\x02\x77\x00\x01\x02\x03\x11\x04\x05\x21\x31\x06\x12\x41"
"\x51\x07\x61\x71\x13\x22\x32\x81\x08\x14\x42\x91\xa1\xb1\xc1\x09\x23\x33"
"\x52\xf0\x15\x62\x72\xd1\x0a\x16\x24\x34\xe1\x25\xf1\x17\x18\x19\x1a\x26"
"\x27\x28\x29\x2a\x35\x36\x37\x38\x39\x3a\x43\x44\x45\x46\x47\x48\x49\x4a"
"\x53\x54\x55\x56\x57\x58\x59\x5a\x63\x64\x65\x66\x67\x68\x69\x6a\x73\x74"
"\x75\x76\x77\x78\x79\x7a\x82\x83\x84\x85\x86\x87\x88\x89\x8a\x92\x93\x94"
"\x95\x96\x97\x98\x99\x9a\xa2\xa3\xa4\xa5\xa6\xa7\xa8\xa9\xaa\xb2\xb3\xb4"
"\xb5\xb6\xb7\xb8\xb9\xba\xc2\xc3\xc4\xc5\xc6\xc7\xc8\xc9\xca\xd2\xd3\xd4"
"\xd5\xd6\xd7\xd8\xd9\xda\xe2\xe3\xe4\xe5\xe6\xe7\xe8\xe9\xea\xf2\xf3\xf4"
"\xf5\xf6\xf7\xf8\xf9\xfa\xff\xda\x00\x0c\x03\x01\x00\x02\x11\x03\x11\x00"
"\x3f\x00";
const char footer[] = "\xff\xd9";
auto real = QByteArray(header, sizeof(header) - 1);
real[164] = bytes[1];
real[166] = bytes[2];
const auto ready = real
+ bytes.mid(3)
+ QByteArray::fromRawData(footer, sizeof(footer) - 1);
return App::readImage(ready);
}
void ClearRemote() { void ClearRemote() {
base::take(StorageImages); base::take(StorageImages);
base::take(WebUrlImages); base::take(WebUrlImages);
@ -251,54 +301,7 @@ ImagePtr CreateFromPhotoSize(
data.vh().v), data.vh().v),
bytes); bytes);
}, [&](const MTPDphotoStrippedSize &data) { }, [&](const MTPDphotoStrippedSize &data) {
const auto bytes = qba(data.vbytes()); auto image = FromInlineBytes(qba(data.vbytes()));
if (bytes.size() < 3 || bytes[0] != '\x01') {
return ImagePtr();
}
const char header[] = "\xff\xd8\xff\xe0\x00\x10\x4a\x46\x49"
"\x46\x00\x01\x01\x00\x00\x01\x00\x01\x00\x00\xff\xdb\x00\x43\x00\x28\x1c"
"\x1e\x23\x1e\x19\x28\x23\x21\x23\x2d\x2b\x28\x30\x3c\x64\x41\x3c\x37\x37"
"\x3c\x7b\x58\x5d\x49\x64\x91\x80\x99\x96\x8f\x80\x8c\x8a\xa0\xb4\xe6\xc3"
"\xa0\xaa\xda\xad\x8a\x8c\xc8\xff\xcb\xda\xee\xf5\xff\xff\xff\x9b\xc1\xff"
"\xff\xff\xfa\xff\xe6\xfd\xff\xf8\xff\xdb\x00\x43\x01\x2b\x2d\x2d\x3c\x35"
"\x3c\x76\x41\x41\x76\xf8\xa5\x8c\xa5\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8"
"\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8"
"\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8"
"\xf8\xf8\xf8\xf8\xf8\xff\xc0\x00\x11\x08\x00\x00\x00\x00\x03\x01\x22\x00"
"\x02\x11\x01\x03\x11\x01\xff\xc4\x00\x1f\x00\x00\x01\x05\x01\x01\x01\x01"
"\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x01\x02\x03\x04\x05\x06\x07\x08"
"\x09\x0a\x0b\xff\xc4\x00\xb5\x10\x00\x02\x01\x03\x03\x02\x04\x03\x05\x05"
"\x04\x04\x00\x00\x01\x7d\x01\x02\x03\x00\x04\x11\x05\x12\x21\x31\x41\x06"
"\x13\x51\x61\x07\x22\x71\x14\x32\x81\x91\xa1\x08\x23\x42\xb1\xc1\x15\x52"
"\xd1\xf0\x24\x33\x62\x72\x82\x09\x0a\x16\x17\x18\x19\x1a\x25\x26\x27\x28"
"\x29\x2a\x34\x35\x36\x37\x38\x39\x3a\x43\x44\x45\x46\x47\x48\x49\x4a\x53"
"\x54\x55\x56\x57\x58\x59\x5a\x63\x64\x65\x66\x67\x68\x69\x6a\x73\x74\x75"
"\x76\x77\x78\x79\x7a\x83\x84\x85\x86\x87\x88\x89\x8a\x92\x93\x94\x95\x96"
"\x97\x98\x99\x9a\xa2\xa3\xa4\xa5\xa6\xa7\xa8\xa9\xaa\xb2\xb3\xb4\xb5\xb6"
"\xb7\xb8\xb9\xba\xc2\xc3\xc4\xc5\xc6\xc7\xc8\xc9\xca\xd2\xd3\xd4\xd5\xd6"
"\xd7\xd8\xd9\xda\xe1\xe2\xe3\xe4\xe5\xe6\xe7\xe8\xe9\xea\xf1\xf2\xf3\xf4"
"\xf5\xf6\xf7\xf8\xf9\xfa\xff\xc4\x00\x1f\x01\x00\x03\x01\x01\x01\x01\x01"
"\x01\x01\x01\x01\x00\x00\x00\x00\x00\x00\x01\x02\x03\x04\x05\x06\x07\x08"
"\x09\x0a\x0b\xff\xc4\x00\xb5\x11\x00\x02\x01\x02\x04\x04\x03\x04\x07\x05"
"\x04\x04\x00\x01\x02\x77\x00\x01\x02\x03\x11\x04\x05\x21\x31\x06\x12\x41"
"\x51\x07\x61\x71\x13\x22\x32\x81\x08\x14\x42\x91\xa1\xb1\xc1\x09\x23\x33"
"\x52\xf0\x15\x62\x72\xd1\x0a\x16\x24\x34\xe1\x25\xf1\x17\x18\x19\x1a\x26"
"\x27\x28\x29\x2a\x35\x36\x37\x38\x39\x3a\x43\x44\x45\x46\x47\x48\x49\x4a"
"\x53\x54\x55\x56\x57\x58\x59\x5a\x63\x64\x65\x66\x67\x68\x69\x6a\x73\x74"
"\x75\x76\x77\x78\x79\x7a\x82\x83\x84\x85\x86\x87\x88\x89\x8a\x92\x93\x94"
"\x95\x96\x97\x98\x99\x9a\xa2\xa3\xa4\xa5\xa6\xa7\xa8\xa9\xaa\xb2\xb3\xb4"
"\xb5\xb6\xb7\xb8\xb9\xba\xc2\xc3\xc4\xc5\xc6\xc7\xc8\xc9\xca\xd2\xd3\xd4"
"\xd5\xd6\xd7\xd8\xd9\xda\xe2\xe3\xe4\xe5\xe6\xe7\xe8\xe9\xea\xf2\xf3\xf4"
"\xf5\xf6\xf7\xf8\xf9\xfa\xff\xda\x00\x0c\x03\x01\x00\x02\x11\x03\x11\x00"
"\x3f\x00";
const char footer[] = "\xff\xd9";
auto real = QByteArray(header, sizeof(header) - 1);
real[164] = bytes[1];
real[166] = bytes[2];
const auto ready = real
+ bytes.mid(3)
+ QByteArray::fromRawData(footer, sizeof(footer) - 1);
auto image = App::readImage(ready);
return !image.isNull() return !image.isNull()
? Images::Create(std::move(image), "JPG") ? Images::Create(std::move(image), "JPG")
: ImagePtr(); : ImagePtr();

View File

@ -13,6 +13,8 @@ class HistoryItem;
namespace Images { namespace Images {
[[nodiscard]] QImage FromInlineBytes(const QByteArray &bytes);
void ClearRemote(); void ClearRemote();
void ClearAll(); void ClearAll();

View File

@ -9,6 +9,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "data/data_photo.h" #include "data/data_photo.h"
#include "data/data_document.h" #include "data/data_document.h"
#include "data/data_document_media.h"
#include "ui/image/image.h" #include "ui/image/image.h"
#include "ui/emoji_config.h" #include "ui/emoji_config.h"
#include "lottie/lottie_single_player.h" #include "lottie/lottie_single_player.h"
@ -114,6 +115,7 @@ void MediaPreviewWidget::showPreview(
_origin = origin; _origin = origin;
_photo = nullptr; _photo = nullptr;
_document = document; _document = document;
_documentMedia = _document->createMediaView();
fillEmojiString(); fillEmojiString();
resetGifAndCache(); resetGifAndCache();
} }
@ -125,6 +127,7 @@ void MediaPreviewWidget::showPreview(
_origin = origin; _origin = origin;
_photo = photo; _photo = photo;
_document = nullptr; _document = nullptr;
_documentMedia = nullptr;
fillEmojiString(); fillEmojiString();
resetGifAndCache(); resetGifAndCache();
} }
@ -137,7 +140,7 @@ void MediaPreviewWidget::startShow() {
_controller->enableGifPauseReason(Window::GifPauseReason::MediaPreview); _controller->enableGifPauseReason(Window::GifPauseReason::MediaPreview);
} }
_hiding = false; _hiding = false;
_a_shown.start([this] { update(); }, 0., 1., st::stickerPreviewDuration); _a_shown.start([=] { update(); }, 0., 1., st::stickerPreviewDuration);
} else { } else {
update(); update();
} }
@ -149,9 +152,10 @@ void MediaPreviewWidget::hidePreview() {
} }
if (_gif) _cache = currentImage(); if (_gif) _cache = currentImage();
_hiding = true; _hiding = true;
_a_shown.start([this] { update(); }, 1., 0., st::stickerPreviewDuration); _a_shown.start([=] { update(); }, 1., 0., st::stickerPreviewDuration);
_photo = nullptr; _photo = nullptr;
_document = nullptr; _document = nullptr;
_documentMedia = nullptr;
resetGifAndCache(); resetGifAndCache();
} }
@ -279,7 +283,7 @@ QPixmap MediaPreviewWidget::currentImage() const {
if (_document->thumbnail()->loaded()) { if (_document->thumbnail()->loaded()) {
_cache = _document->thumbnail()->pixBlurred(_origin, s.width(), s.height()); _cache = _document->thumbnail()->pixBlurred(_origin, s.width(), s.height());
_cacheStatus = CacheThumbLoaded; _cacheStatus = CacheThumbLoaded;
} else if (const auto blurred = _document->thumbnailInline()) { } else if (const auto blurred = _documentMedia->thumbnailInline()) {
_cache = _document->thumbnail()->pixBlurred(_origin, s.width(), s.height()); _cache = _document->thumbnail()->pixBlurred(_origin, s.width(), s.height());
_cacheStatus = CacheThumbLoaded; _cacheStatus = CacheThumbLoaded;
} else { } else {

View File

@ -12,6 +12,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "data/data_file_origin.h" #include "data/data_file_origin.h"
#include "ui/rp_widget.h" #include "ui/rp_widget.h"
namespace Data {
class DocumentMedia;
} // namespace Data
namespace Lottie { namespace Lottie {
class SinglePlayer; class SinglePlayer;
} // namespace Lottie } // namespace Lottie
@ -55,6 +59,7 @@ private:
bool _hiding = false; bool _hiding = false;
Data::FileOrigin _origin; Data::FileOrigin _origin;
DocumentData *_document = nullptr; DocumentData *_document = nullptr;
std::shared_ptr<Data::DocumentMedia> _documentMedia;
PhotoData *_photo = nullptr; PhotoData *_photo = nullptr;
Media::Clip::ReaderPointer _gif; Media::Clip::ReaderPointer _gif;
std::unique_ptr<Lottie::SinglePlayer> _lottie; std::unique_ptr<Lottie::SinglePlayer> _lottie;