Use Data::DocumentMedia to store good thumbnails.

This commit is contained in:
John Preston 2020-03-27 15:40:50 +04:00
parent 61647275e8
commit 7db53599e8
29 changed files with 545 additions and 168 deletions

View File

@ -360,6 +360,8 @@ PRIVATE
data/data_document.h data/data_document.h
data/data_document_good_thumbnail.cpp data/data_document_good_thumbnail.cpp
data/data_document_good_thumbnail.h data/data_document_good_thumbnail.h
data/data_document_media.cpp
data/data_document_media.h
data/data_drafts.cpp data/data_drafts.cpp
data/data_drafts.h data/data_drafts.h
data/data_folder.cpp data/data_folder.cpp

View File

@ -10,6 +10,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "data/data_session.h" #include "data/data_session.h"
#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 "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"
@ -569,7 +570,6 @@ void DocumentData::setattributes(
_additional = nullptr; _additional = nullptr;
} }
} }
validateGoodThumbnail();
if (isAudioFile() || isAnimation() || isVoiceMessage()) { if (isAudioFile() || isAnimation() || isVoiceMessage()) {
setMaybeSupportsStreaming(true); setMaybeSupportsStreaming(true);
} }
@ -612,7 +612,6 @@ bool DocumentData::checkWallPaperProperties() {
return false; // #TODO themes support svg patterns return false; // #TODO themes support svg patterns
} }
type = WallPaperDocument; type = WallPaperDocument;
validateGoodThumbnail();
return true; return true;
} }
@ -661,48 +660,65 @@ Storage::Cache::Key DocumentData::goodThumbnailCacheKey() const {
return Data::DocumentThumbCacheKey(_dc, id); return Data::DocumentThumbCacheKey(_dc, id);
} }
Image *DocumentData::goodThumbnail() const { bool DocumentData::goodThumbnailChecked() const {
return _goodThumbnail.get(); return (_goodThumbnailState & GoodThumbnailFlag::Mask)
== GoodThumbnailFlag::Checked;
} }
void DocumentData::validateGoodThumbnail() { bool DocumentData::goodThumbnailGenerating() const {
if (!isVideoFile() return (_goodThumbnailState & GoodThumbnailFlag::Mask)
&& !isAnimation() == GoodThumbnailFlag::Generating;
&& !isWallPaper()
&& !isTheme()
&& (!sticker() || !sticker()->animated)) {
_goodThumbnail = nullptr;
} else if (!_goodThumbnail && hasRemoteLocation()) {
_goodThumbnail = std::make_unique<Image>(
std::make_unique<Data::GoodThumbSource>(this));
}
} }
void DocumentData::refreshGoodThumbnail() { bool DocumentData::goodThumbnailNoData() const {
if (_goodThumbnail && hasRemoteLocation()) { return (_goodThumbnailState & GoodThumbnailFlag::Mask)
replaceGoodThumbnail(std::make_unique<Data::GoodThumbSource>(this)); == GoodThumbnailFlag::NoData;
}
} }
void DocumentData::replaceGoodThumbnail( void DocumentData::setGoodThumbnailGenerating() {
std::unique_ptr<Images::Source> &&source) { _goodThumbnailState = (_goodThumbnailState & ~GoodThumbnailFlag::Mask)
_goodThumbnail->replaceSource(std::move(source)); | GoodThumbnailFlag::Generating;
} }
void DocumentData::setGoodThumbnailOnUpload( void DocumentData::setGoodThumbnailDataReady() {
QImage &&image, _goodThumbnailState = GoodThumbnailFlag::DataReady
QByteArray &&bytes) { | (goodThumbnailNoData()
Expects(uploadingData != nullptr); ? GoodThumbnailFlag(0)
: (_goodThumbnailState & GoodThumbnailFlag::Mask));
}
if (image.isNull()) { void DocumentData::setGoodThumbnailChecked(bool hasData) {
if (!hasData && (_goodThumbnailState & GoodThumbnailFlag::DataReady)) {
_goodThumbnailState &= ~GoodThumbnailFlag::DataReady;
_goodThumbnailState &= ~GoodThumbnailFlag::Mask;
Data::DocumentMedia::CheckGoodThumbnail(this);
return; return;
} }
_goodThumbnail = std::make_unique<Image>( _goodThumbnailState = (_goodThumbnailState & ~GoodThumbnailFlag::Mask)
std::make_unique<Images::LocalFileSource>( | (hasData
QString(), ? GoodThumbnailFlag::Checked
std::move(bytes), : GoodThumbnailFlag::NoData);
sticker() ? "WEBP" : "JPG", }
std::move(image)));
std::shared_ptr<Data::DocumentMedia> DocumentData::createMediaView() {
if (auto result = activeMediaView()) {
return result;
}
auto result = std::make_shared<Data::DocumentMedia>(this);
_media = result;
return result;
}
std::shared_ptr<Data::DocumentMedia> DocumentData::activeMediaView() {
return _media.lock();
}
void DocumentData::setGoodThumbnailPhoto(not_null<PhotoData*> photo) {
_goodThumbnailPhoto = photo;
}
PhotoData *DocumentData::goodThumbnailPhoto() const {
return _goodThumbnailPhoto;
} }
auto DocumentData::bigFileBaseCacheKey() const auto DocumentData::bigFileBaseCacheKey() const
@ -817,7 +833,8 @@ bool DocumentData::loaded(FilePathResolve resolve) const {
ActiveCache().increment(ComputeUsage(that->sticker())); ActiveCache().increment(ComputeUsage(that->sticker()));
} }
that->refreshGoodThumbnail(); that->setGoodThumbnailDataReady();
Data::DocumentMedia::CheckGoodThumbnail(that);
destroyLoader(); destroyLoader();
if (!that->_data.isEmpty() || that->getStickerLarge()) { if (!that->_data.isEmpty() || that->getStickerLarge()) {
@ -1606,7 +1623,6 @@ void DocumentData::setRemoteLocation(
} }
} }
} }
validateGoodThumbnail();
} }
void DocumentData::setContentUrl(const QString &url) { void DocumentData::setContentUrl(const QString &url) {

View File

@ -32,6 +32,7 @@ class Loader;
namespace Data { namespace Data {
class Session; class Session;
class DocumentMedia;
} // namespace Data } // namespace Data
namespace Main { namespace Main {
@ -180,11 +181,18 @@ public:
ImagePtr thumbnailInline, ImagePtr thumbnailInline,
ImagePtr thumbnail); ImagePtr thumbnail);
[[nodiscard]] Image *goodThumbnail() const;
[[nodiscard]] Storage::Cache::Key goodThumbnailCacheKey() const; [[nodiscard]] Storage::Cache::Key goodThumbnailCacheKey() const;
void setGoodThumbnailOnUpload(QImage &&image, QByteArray &&bytes); [[nodiscard]] bool goodThumbnailChecked() const;
void refreshGoodThumbnail(); [[nodiscard]] bool goodThumbnailGenerating() const;
void replaceGoodThumbnail(std::unique_ptr<Images::Source> &&source); [[nodiscard]] bool goodThumbnailNoData() const;
void setGoodThumbnailGenerating();
void setGoodThumbnailDataReady();
void setGoodThumbnailChecked(bool hasData);
[[nodiscard]] std::shared_ptr<Data::DocumentMedia> createMediaView();
[[nodiscard]] std::shared_ptr<Data::DocumentMedia> activeMediaView();
void setGoodThumbnailPhoto(not_null<PhotoData*> photo);
[[nodiscard]] PhotoData *goodThumbnailPhoto() const;
[[nodiscard]] auto bigFileBaseCacheKey() const [[nodiscard]] auto bigFileBaseCacheKey() const
-> std::optional<Storage::Cache::Key>; -> std::optional<Storage::Cache::Key>;
@ -258,6 +266,17 @@ private:
using Flags = base::flags<Flag>; using Flags = base::flags<Flag>;
friend constexpr bool is_flag_type(Flag) { return true; }; friend constexpr bool is_flag_type(Flag) { return true; };
enum class GoodThumbnailFlag : uchar {
Checked = 0x01,
Generating = 0x02,
NoData = 0x03,
Mask = 0x03,
DataReady = 0x04,
};
using GoodThumbnailState = base::flags<GoodThumbnailFlag>;
friend constexpr bool is_flag_type(GoodThumbnailFlag) { return true; };
static constexpr Flags kStreamingSupportedMask = Flags() static constexpr Flags kStreamingSupportedMask = Flags()
| Flag::StreamingMaybeYes | Flag::StreamingMaybeYes
| Flag::StreamingMaybeNo; | Flag::StreamingMaybeNo;
@ -272,9 +291,8 @@ private:
friend class Serialize::Document; friend class Serialize::Document;
LocationType locationType() const; [[nodiscard]] LocationType locationType() const;
void validateLottieSticker(); void validateLottieSticker();
void validateGoodThumbnail();
void setMaybeSupportsStreaming(bool supports); void setMaybeSupportsStreaming(bool supports);
void setLoadedInMediaCacheLocation(); void setLoadedInMediaCacheLocation();
@ -294,8 +312,9 @@ private:
ImagePtr _thumbnailInline; ImagePtr _thumbnailInline;
ImagePtr _thumbnail; ImagePtr _thumbnail;
std::unique_ptr<Image> _goodThumbnail;
Data::ReplyPreview _replyPreview; Data::ReplyPreview _replyPreview;
std::weak_ptr<Data::DocumentMedia> _media;
PhotoData *_goodThumbnailPhoto = nullptr;
not_null<Data::Session*> _owner; not_null<Data::Session*> _owner;
@ -304,6 +323,7 @@ private:
std::unique_ptr<DocumentAdditionalData> _additional; std::unique_ptr<DocumentAdditionalData> _additional;
int32 _duration = -1; int32 _duration = -1;
mutable Flags _flags = kStreamingSupportedUnknown; mutable Flags _flags = kStreamingSupportedUnknown;
GoodThumbnailState _goodThumbnailState = GoodThumbnailState();
mutable std::unique_ptr<FileLoader> _loader; mutable std::unique_ptr<FileLoader> _loader;
}; };

View File

@ -0,0 +1,202 @@
/*
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_document_media.h"
#include "data/data_document.h"
#include "data/data_document_good_thumbnail.h"
#include "data/data_session.h"
#include "data/data_cloud_themes.h"
#include "media/clip/media_clip_reader.h"
#include "main/main_session.h"
#include "lottie/lottie_animation.h"
#include "window/themes/window_theme_preview.h"
#include "ui/image/image.h"
#include "app.h"
#include <QtCore/QBuffer>
#include <QtGui/QImageReader>
namespace Data {
namespace {
constexpr auto kReadAreaLimit = 12'032 * 9'024;
constexpr auto kWallPaperThumbnailLimit = 960;
constexpr auto kMaxVideoFrameArea = 7'680 * 4'320;
constexpr auto kGoodThumbQuality = 87;
enum class FileType {
Video,
AnimatedSticker,
WallPaper,
Theme,
};
[[nodiscard]] bool MayHaveGoodThumbnail(not_null<DocumentData*> owner) {
return owner->isVideoFile()
|| owner->isAnimation()
|| owner->isWallPaper()
|| owner->isTheme()
|| (owner->sticker() && owner->sticker()->animated);
}
[[nodiscard]] QImage PrepareGoodThumbnail(
const QString &path,
QByteArray data,
FileType type) {
if (type == FileType::Video) {
return ::Media::Clip::PrepareForSending(path, data).thumbnail;
} else if (type == FileType::AnimatedSticker) {
return Lottie::ReadThumbnail(Lottie::ReadContent(data, path));
} else if (type == FileType::Theme) {
return Window::Theme::GeneratePreview(data, path);
}
auto buffer = QBuffer(&data);
auto file = QFile(path);
auto device = data.isEmpty() ? static_cast<QIODevice*>(&file) : &buffer;
auto reader = QImageReader(device);
const auto size = reader.size();
if (!reader.canRead()
|| (size.width() * size.height() > kReadAreaLimit)) {
return QImage();
}
auto result = reader.read();
if (!result.width() || !result.height()) {
return QImage();
}
return (result.width() > kWallPaperThumbnailLimit
|| result.height() > kWallPaperThumbnailLimit)
? result.scaled(
kWallPaperThumbnailLimit,
kWallPaperThumbnailLimit,
Qt::KeepAspectRatio,
Qt::SmoothTransformation)
: result;
}
} // namespace
DocumentMedia::DocumentMedia(not_null<DocumentData*> owner)
: _owner(owner) {
}
DocumentMedia::~DocumentMedia() = default;
void DocumentMedia::goodThumbnailWanted() {
_flags |= Flag::GoodThumbnailWanted;
}
Image *DocumentMedia::goodThumbnail() const {
Expects((_flags & Flag::GoodThumbnailWanted) != 0);
if (!_goodThumbnail) {
ReadOrGenerateThumbnail(_owner);
}
return _goodThumbnail.get();
}
void DocumentMedia::setGoodThumbnail(QImage thumbnail) {
if (!(_flags & Flag::GoodThumbnailWanted)) {
return;
}
_goodThumbnail = std::make_unique<Image>(
std::make_unique<Images::ImageSource>(std::move(thumbnail), "PNG"));
_owner->session().downloaderTaskFinished().notify();
}
void DocumentMedia::GenerateGoodThumbnail(not_null<DocumentData*> document) {
const auto data = document->data();
const auto type = document->isWallPaper()
? FileType::WallPaper
: document->isTheme()
? FileType::Theme
: document->sticker()
? FileType::AnimatedSticker
: FileType::Video;
auto location = document->location().isEmpty()
? nullptr
: std::make_unique<FileLocation>(document->location());
if (data.isEmpty() && !location) {
document->setGoodThumbnailChecked(false);
return;
}
const auto guard = base::make_weak(&document->owner().session());
crl::async([=, location = std::move(location)] {
const auto filepath = (location && location->accessEnable())
? location->name()
: QString();
auto result = PrepareGoodThumbnail(filepath, data, type);
auto bytes = QByteArray();
if (!result.isNull()) {
auto buffer = QBuffer(&bytes);
const auto format = (type == FileType::AnimatedSticker)
? "WEBP"
: (type == FileType::WallPaper && result.hasAlphaChannel())
? "PNG"
: "JPG";
result.save(&buffer, format, kGoodThumbQuality);
}
if (!filepath.isEmpty()) {
location->accessDisable();
}
const auto cache = bytes.isEmpty() ? QByteArray("(failed)") : bytes;
crl::on_main(guard, [=] {
document->setGoodThumbnailChecked(true);
if (const auto active = document->activeMediaView()) {
active->setGoodThumbnail(result);
}
document->owner().cache().put(
document->goodThumbnailCacheKey(),
Storage::Cache::Database::TaggedValue{
base::duplicate(cache),
kImageCacheTag });
});
});
}
void DocumentMedia::CheckGoodThumbnail(not_null<DocumentData*> document) {
if (!document->goodThumbnailChecked()) {
ReadOrGenerateThumbnail(document);
}
}
void DocumentMedia::ReadOrGenerateThumbnail(
not_null<DocumentData*> document) {
if (document->goodThumbnailGenerating()
|| document->goodThumbnailNoData()
|| !MayHaveGoodThumbnail(document)) {
return;
}
document->setGoodThumbnailGenerating();
const auto guard = base::make_weak(&document->session());
const auto active = document->activeMediaView();
const auto got = [=](QByteArray value) {
if (value.isEmpty()) {
crl::on_main(guard, [=] {
GenerateGoodThumbnail(document);
});
} else if (active) {
crl::async([=] {
const auto image = App::readImage(value, nullptr, false);
crl::on_main(guard, [=] {
document->setGoodThumbnailChecked(true);
if (const auto active = document->activeMediaView()) {
active->setGoodThumbnail(image);
}
});
});
} else {
crl::on_main(guard, [=] {
document->setGoodThumbnailChecked(true);
});
}
};
document->owner().cache().get(document->goodThumbnailCacheKey(), got);
}
} // namespace Data

View File

@ -0,0 +1,43 @@
/*
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
#include "base/flags.h"
namespace Data {
class DocumentMedia final {
public:
explicit DocumentMedia(not_null<DocumentData*> owner);
~DocumentMedia();
void goodThumbnailWanted();
[[nodiscard]] Image *goodThumbnail() const;
void setGoodThumbnail(QImage thumbnail);
// For DocumentData.
void validateGoodThumbnail();
static void CheckGoodThumbnail(not_null<DocumentData*> document);
private:
enum class Flag : uchar {
GoodThumbnailWanted = 0x01,
};
inline constexpr bool is_flag_type(Flag) { return true; };
using Flags = base::flags<Flag>;
static void ReadOrGenerateThumbnail(not_null<DocumentData*> document);
static void GenerateGoodThumbnail(not_null<DocumentData*> document);
const not_null<DocumentData*> _owner;
std::unique_ptr<Image> _goodThumbnail;
Flags _flags;
};
} // namespace Data

View File

@ -750,23 +750,6 @@ bool MediaFile::updateSentMedia(const MTPMessageMedia &media) {
return false; return false;
} }
parent()->history()->owner().documentConvert(_document, *content); parent()->history()->owner().documentConvert(_document, *content);
if (const auto good = _document->goodThumbnail()) {
auto bytes = good->bytesForCache();
if (const auto length = bytes.size()) {
if (length > Storage::kMaxFileInMemory) {
LOG(("App Error: Bad thumbnail data for saving to cache."));
} else {
parent()->history()->owner().cache().putIfEmpty(
_document->goodThumbnailCacheKey(),
Storage::Cache::Database::TaggedValue(
std::move(bytes),
Data::kImageCacheTag));
_document->refreshGoodThumbnail();
}
}
}
return true; return true;
} }

View File

@ -2421,6 +2421,7 @@ void Session::documentConvert(
}(); }();
const auto oldKey = original->mediaKey(); const auto oldKey = original->mediaKey();
const auto oldCacheKey = original->cacheKey(); const auto oldCacheKey = original->cacheKey();
const auto oldGoodKey = original->goodThumbnailCacheKey();
const auto idChanged = (original->id != id); const auto idChanged = (original->id != id);
const auto sentSticker = idChanged && (original->sticker() != nullptr); const auto sentSticker = idChanged && (original->sticker() != nullptr);
if (idChanged) { if (idChanged) {
@ -2444,6 +2445,7 @@ void Session::documentConvert(
documentApplyFields(original, data); documentApplyFields(original, data);
if (idChanged) { if (idChanged) {
cache().moveIfEmpty(oldCacheKey, original->cacheKey()); cache().moveIfEmpty(oldCacheKey, original->cacheKey());
cache().moveIfEmpty(oldGoodKey, original->goodThumbnailCacheKey());
if (savedGifs().indexOf(original) >= 0) { if (savedGifs().indexOf(original) >= 0) {
Local::writeSavedGifs(); Local::writeSavedGifs();
} }
@ -3180,10 +3182,10 @@ void Session::unregisterPlayingVideoFile(not_null<ViewElement*> view) {
if (i != _playingVideoFiles.end()) { if (i != _playingVideoFiles.end()) {
if (!--i->second) { if (!--i->second) {
_playingVideoFiles.erase(i); _playingVideoFiles.erase(i);
unregisterHeavyViewPart(view); view->checkHeavyPart();
} }
} else { } else {
unregisterHeavyViewPart(view); view->checkHeavyPart();
} }
} }
@ -3205,7 +3207,7 @@ void Session::checkPlayingVideoFiles() {
continue; continue;
} }
} }
unregisterHeavyViewPart(view); view->checkHeavyPart();
} }
} }

View File

@ -237,15 +237,7 @@ bool WebPageData::applyChanges(
} }
void WebPageData::replaceDocumentGoodThumbnail() { void WebPageData::replaceDocumentGoodThumbnail() {
if (!document || !photo || !document->goodThumbnail()) { if (document && photo) {
return; document->setGoodThumbnailPhoto(photo);
} }
const auto &location = photo->large()->location();
if (location.valid()) {
document->replaceGoodThumbnail(
std::make_unique<Images::StorageSource>(
location,
photo->large()->bytesSize()));
}
} }

View File

@ -66,7 +66,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
namespace { namespace {
constexpr auto kScrollDateHideTimeout = 1000; constexpr auto kScrollDateHideTimeout = 1000;
constexpr auto kUnloadHeavyPartsPages = 1; constexpr auto kUnloadHeavyPartsPages = 2;
// Helper binary search for an item in a list that is not completely // Helper binary search for an item in a list that is not completely
// above the given top of the visible area or below the given bottom of the visible area // above the given top of the visible area or below the given bottom of the visible area

View File

@ -606,6 +606,12 @@ auto Element::verticalRepaintRange() const -> VerticalRepaintRange {
}; };
} }
void Element::checkHeavyPart() {
if (_media) {
_media->checkHeavyPart();
}
}
void Element::unloadHeavyPart() { void Element::unloadHeavyPart() {
if (_media) { if (_media) {
_media->unloadHeavyPart(); _media->unloadHeavyPart();

View File

@ -261,7 +261,8 @@ public:
}; };
[[nodiscard]] virtual VerticalRepaintRange verticalRepaintRange() const; [[nodiscard]] virtual VerticalRepaintRange verticalRepaintRange() const;
virtual void unloadHeavyPart(); void checkHeavyPart();
void unloadHeavyPart();
// Legacy blocks structure. // Legacy blocks structure.
HistoryBlock *block(); HistoryBlock *block();

View File

@ -32,6 +32,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "data/data_streaming.h" #include "data/data_streaming.h"
#include "data/data_document.h" #include "data/data_document.h"
#include "data/data_file_origin.h" #include "data/data_file_origin.h"
#include "data/data_document_media.h"
#include "app.h" #include "app.h"
#include "styles/style_history.h" #include "styles/style_history.h"
@ -92,6 +93,8 @@ Gif::~Gif() {
_data->owner().streaming().keepAlive(_data); _data->owner().streaming().keepAlive(_data);
setStreamed(nullptr); setStreamed(nullptr);
} }
_dataMedia = nullptr;
checkHeavyPart();
} }
QSize Gif::sizeForAspectRatio() const { QSize Gif::sizeForAspectRatio() const {
@ -404,7 +407,8 @@ void Gif::draw(Painter &p, const QRect &r, TextSelection selection, crl::time ms
} }
} }
} else { } else {
const auto good = _data->goodThumbnail(); ensureDataMediaCreated();
const auto good = _dataMedia->goodThumbnail();
if (good && good->loaded()) { if (good && good->loaded()) {
p.drawPixmap(rthumb.topLeft(), good->pixSingle({}, _thumbw, _thumbh, usew, painth, roundRadius, roundCorners)); p.drawPixmap(rthumb.topLeft(), good->pixSingle({}, _thumbw, _thumbh, usew, painth, roundRadius, roundCorners));
} else { } else {
@ -1071,6 +1075,15 @@ TextState Gif::getStateGrouped(
: _savel); : _savel);
} }
void Gif::ensureDataMediaCreated() const {
if (_dataMedia) {
return;
}
_dataMedia = _data->createMediaView();
_dataMedia->goodThumbnailWanted();
history()->owner().registerHeavyViewPart(_parent);
}
bool Gif::uploading() const { bool Gif::uploading() const {
return _data->uploading(); return _data->uploading();
} }
@ -1116,7 +1129,10 @@ void Gif::validateGroupedCache(
not_null<uint64*> cacheKey, not_null<uint64*> cacheKey,
not_null<QPixmap*> cache) const { not_null<QPixmap*> cache) const {
using Option = Images::Option; using Option = Images::Option;
const auto good = _data->goodThumbnail();
ensureDataMediaCreated();
const auto good = _dataMedia->goodThumbnail();
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());
@ -1245,6 +1261,17 @@ void Gif::parentTextUpdated() {
} }
} }
void Gif::checkHeavyPart() {
if (!_dataMedia && !_streamed) {
history()->owner().unregisterHeavyViewPart(_parent);
}
}
void Gif::unloadHeavyPart() {
stopAnimation();
_dataMedia = nullptr;
}
void Gif::refreshParentId(not_null<HistoryItem*> realParent) { void Gif::refreshParentId(not_null<HistoryItem*> realParent) {
File::refreshParentId(realParent); File::refreshParentId(realParent);
if (_parent->media() == this) { if (_parent->media() == this) {

View File

@ -15,6 +15,10 @@ struct HistoryMessageReply;
struct HistoryMessageForwarded; struct HistoryMessageForwarded;
class Painter; class Painter;
namespace Data {
class DocumentMedia;
} // namespace Data
namespace Media { namespace Media {
namespace View { namespace View {
class PlaybackProgress; class PlaybackProgress;
@ -100,9 +104,8 @@ public:
void parentTextUpdated() override; void parentTextUpdated() override;
void unloadHeavyPart() override { void checkHeavyPart() override;
stopAnimation(); void unloadHeavyPart() override;
}
void refreshParentId(not_null<HistoryItem*> realParent) override; void refreshParentId(not_null<HistoryItem*> realParent) override;
@ -113,6 +116,7 @@ private:
bool dataFinished() const override; bool dataFinished() const override;
bool dataLoaded() const override; bool dataLoaded() const override;
void ensureDataMediaCreated() const;
void refreshCaption(); void refreshCaption();
[[nodiscard]] bool autoplayEnabled() const; [[nodiscard]] bool autoplayEnabled() const;
@ -161,11 +165,12 @@ private:
StateRequest request, StateRequest request,
QPoint position) const; QPoint position) const;
not_null<DocumentData*> _data; const not_null<DocumentData*> _data;
int _thumbw = 1; int _thumbw = 1;
int _thumbh = 1; int _thumbh = 1;
Ui::Text::String _caption; Ui::Text::String _caption;
std::unique_ptr<Streamed> _streamed; std::unique_ptr<Streamed> _streamed;
mutable std::shared_ptr<Data::DocumentMedia> _dataMedia;
QString _downloadSize; QString _downloadSize;

View File

@ -253,6 +253,8 @@ public:
crl::time ms) const { crl::time ms) const {
} }
virtual void checkHeavyPart() {
}
virtual void unloadHeavyPart() { virtual void unloadHeavyPart() {
} }

View File

@ -422,6 +422,12 @@ int GroupedMedia::checkAnimationCount() {
return result; return result;
} }
void GroupedMedia::checkHeavyPart() {
for (auto &part : _parts) {
part.content->checkHeavyPart();
}
}
void GroupedMedia::unloadHeavyPart() { void GroupedMedia::unloadHeavyPart() {
for (auto &part : _parts) { for (auto &part : _parts) {
part.content->unloadHeavyPart(); part.content->unloadHeavyPart();

View File

@ -88,6 +88,7 @@ public:
void stopAnimation() override; void stopAnimation() override;
int checkAnimationCount() override; int checkAnimationCount() override;
void checkHeavyPart() override;
void unloadHeavyPart() override; void unloadHeavyPart() override;
void parentTextUpdated() override; void parentTextUpdated() override;

View File

@ -34,6 +34,8 @@ public:
} }
virtual void clearStickerLoopPlayed() { virtual void clearStickerLoopPlayed() {
} }
virtual void checkHeavyPart() {
}
virtual void unloadHeavyPart() { virtual void unloadHeavyPart() {
} }
virtual void refreshLink() { virtual void refreshLink() {
@ -82,6 +84,9 @@ public:
_content->clearStickerLoopPlayed(); _content->clearStickerLoopPlayed();
} }
void checkHeavyPart() override {
_content->checkHeavyPart();
}
void unloadHeavyPart() override { void unloadHeavyPart() override {
_content->unloadHeavyPart(); _content->unloadHeavyPart();
} }

View File

@ -24,6 +24,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "window/window_session_controller.h" // isGifPausedAtLeastFor. #include "window/window_session_controller.h" // isGifPausedAtLeastFor.
#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_file_origin.h" #include "data/data_file_origin.h"
#include "lottie/lottie_single_player.h" #include "lottie/lottie_single_player.h"
#include "styles/style_history.h" #include "styles/style_history.h"
@ -55,12 +56,12 @@ namespace {
Sticker::Sticker( Sticker::Sticker(
not_null<Element*> parent, not_null<Element*> parent,
not_null<DocumentData*> document, not_null<DocumentData*> data,
const Lottie::ColorReplacements *replacements) const Lottie::ColorReplacements *replacements)
: _parent(parent) : _parent(parent)
, _document(document) , _data(data)
, _replacements(replacements) { , _replacements(replacements) {
_document->loadThumbnail(parent->data()->fullId()); _data->loadThumbnail(parent->data()->fullId());
} }
Sticker::~Sticker() { Sticker::~Sticker() {
@ -72,9 +73,9 @@ bool Sticker::isEmojiSticker() const {
} }
void Sticker::initSize() { void Sticker::initSize() {
_size = _document->dimensions; _size = _data->dimensions;
if (isEmojiSticker() || _diceIndex >= 0) { if (isEmojiSticker() || _diceIndex >= 0) {
_size = GetAnimatedEmojiSize(&_document->session(), _size); _size = GetAnimatedEmojiSize(&_data->session(), _size);
[[maybe_unused]] bool result = readyToDrawLottie(); [[maybe_unused]] bool result = readyToDrawLottie();
} else { } else {
_size = DownscaledSize( _size = DownscaledSize(
@ -92,13 +93,13 @@ bool Sticker::readyToDrawLottie() {
if (!_lastDiceFrame.isNull()) { if (!_lastDiceFrame.isNull()) {
return true; return true;
} }
const auto sticker = _document->sticker(); const auto sticker = _data->sticker();
if (!sticker) { if (!sticker) {
return false; return false;
} }
_document->checkStickerLarge(); _data->checkStickerLarge();
const auto loaded = _document->loaded(); const auto loaded = _data->loaded();
if (sticker->animated && !_lottie && loaded) { if (sticker->animated && !_lottie && loaded) {
setupLottie(); setupLottie();
} }
@ -123,8 +124,8 @@ QSize Sticker::GetAnimatedEmojiSize(
void Sticker::draw(Painter &p, const QRect &r, bool selected) { void Sticker::draw(Painter &p, const QRect &r, bool selected) {
if (readyToDrawLottie()) { if (readyToDrawLottie()) {
paintLottie(p, r, selected); paintLottie(p, r, selected);
} else if (_document->sticker() } else if (_data->sticker()
&& (!_document->sticker()->animated || !_replacements)) { && (!_data->sticker()->animated || !_replacements)) {
paintPixmap(p, r, selected); paintPixmap(p, r, selected);
} }
} }
@ -166,7 +167,7 @@ void Sticker::paintLottie(Painter &p, const QRect &r, bool selected) {
: (_diceIndex == 0) : (_diceIndex == 0)
? false ? false
: (isEmojiSticker() : (isEmojiSticker()
|| !_document->session().settings().loopAnimatedStickers()); || !_data->session().settings().loopAnimatedStickers());
const auto count = _lottie->information().framesCount; const auto count = _lottie->information().framesCount;
_atTheEnd = (frame.index + 1 == count); _atTheEnd = (frame.index + 1 == count);
_nextLastDiceFrame = !paused _nextLastDiceFrame = !paused
@ -197,22 +198,24 @@ void Sticker::paintPixmap(Painter &p, const QRect &r, bool selected) {
} }
QPixmap Sticker::paintedPixmap(bool selected) const { QPixmap Sticker::paintedPixmap(bool selected) const {
ensureDataMediaCreated();
const auto o = _parent->data()->fullId(); const auto o = _parent->data()->fullId();
const auto w = _size.width(); const auto w = _size.width();
const auto h = _size.height(); const auto h = _size.height();
const auto &c = st::msgStickerOverlay; const auto &c = st::msgStickerOverlay;
const auto good = _document->goodThumbnail(); const auto good = _dataMedia->goodThumbnail();
if (good && !good->loaded()) { if (good && !good->loaded()) {
good->load({}); good->load({});
} }
if (const auto image = _document->getStickerLarge()) { if (const auto image = _data->getStickerLarge()) {
return selected return selected
? image->pixColored(o, c, w, h) ? image->pixColored(o, c, w, h)
: image->pix(o, w, h); : image->pix(o, w, h);
// //
// Inline thumbnails can't have alpha channel. // Inline thumbnails can't have alpha channel.
// //
//} else if (const auto blurred = _document->thumbnailInline()) { //} else if (const auto blurred = _data->thumbnailInline()) {
// return selected // return selected
// ? blurred->pixBlurredColored(o, c, w, h) // ? blurred->pixBlurredColored(o, c, w, h)
// : blurred->pixBlurred(o, w, h); // : blurred->pixBlurred(o, w, h);
@ -220,7 +223,7 @@ QPixmap Sticker::paintedPixmap(bool selected) const {
return selected return selected
? good->pixColored(o, c, w, h) ? good->pixColored(o, c, w, h)
: good->pix(o, w, h); : good->pix(o, w, h);
} else if (const auto thumbnail = _document->thumbnail()) { } else if (const auto thumbnail = _data->thumbnail()) {
return selected return selected
? thumbnail->pixBlurredColored(o, c, w, h) ? thumbnail->pixBlurredColored(o, c, w, h)
: thumbnail->pixBlurred(o, w, h); : thumbnail->pixBlurred(o, w, h);
@ -232,7 +235,7 @@ void Sticker::refreshLink() {
if (_link) { if (_link) {
return; return;
} }
const auto sticker = _document->sticker(); const auto sticker = _data->sticker();
if (isEmojiSticker()) { if (isEmojiSticker()) {
const auto weak = base::make_weak(this); const auto weak = base::make_weak(this);
_link = std::make_shared<LambdaClickHandler>([weak] { _link = std::make_shared<LambdaClickHandler>([weak] {
@ -245,12 +248,20 @@ void Sticker::refreshLink() {
that->_parent); that->_parent);
}); });
} else if (sticker && sticker->set.type() != mtpc_inputStickerSetEmpty) { } else if (sticker && sticker->set.type() != mtpc_inputStickerSetEmpty) {
_link = std::make_shared<LambdaClickHandler>([document = _document] { _link = std::make_shared<LambdaClickHandler>([document = _data] {
StickerSetBox::Show(App::wnd()->sessionController(), document); StickerSetBox::Show(App::wnd()->sessionController(), document);
}); });
} }
} }
void Sticker::ensureDataMediaCreated() const {
if (_dataMedia) {
return;
}
_dataMedia = _data->createMediaView();
_dataMedia->goodThumbnailWanted();
}
void Sticker::setDiceIndex(const QString &emoji, int index) { void Sticker::setDiceIndex(const QString &emoji, int index) {
_diceEmoji = emoji; _diceEmoji = emoji;
_diceIndex = index; _diceIndex = index;
@ -258,7 +269,7 @@ void Sticker::setDiceIndex(const QString &emoji, int index) {
void Sticker::setupLottie() { void Sticker::setupLottie() {
_lottie = Stickers::LottiePlayerFromDocument( _lottie = Stickers::LottiePlayerFromDocument(
_document, _data,
_replacements, _replacements,
Stickers::LottieSize::MessageHistory, Stickers::LottieSize::MessageHistory,
_size * cIntRetinaFactor(), _size * cIntRetinaFactor(),

View File

@ -16,6 +16,7 @@ class Session;
namespace Data { namespace Data {
struct FileOrigin; struct FileOrigin;
class DocumentMedia;
} // namespace Data } // namespace Data
namespace Lottie { namespace Lottie {
@ -31,7 +32,7 @@ class Sticker final
public: public:
Sticker( Sticker(
not_null<Element*> parent, not_null<Element*> parent,
not_null<DocumentData*> document, not_null<DocumentData*> data,
const Lottie::ColorReplacements *replacements = nullptr); const Lottie::ColorReplacements *replacements = nullptr);
~Sticker(); ~Sticker();
@ -43,7 +44,7 @@ public:
} }
DocumentData *document() override { DocumentData *document() override {
return _document; return _data;
} }
void clearStickerLoopPlayed() override { void clearStickerLoopPlayed() override {
_lottieOncePlayed = false; _lottieOncePlayed = false;
@ -71,13 +72,16 @@ private:
void paintPixmap(Painter &p, const QRect &r, bool selected); void paintPixmap(Painter &p, const QRect &r, bool selected);
[[nodiscard]] QPixmap paintedPixmap(bool selected) const; [[nodiscard]] QPixmap paintedPixmap(bool selected) const;
void ensureDataMediaCreated() const;
void setupLottie(); void setupLottie();
void unloadLottie(); void unloadLottie();
const not_null<Element*> _parent; const not_null<Element*> _parent;
const not_null<DocumentData*> _document; const not_null<DocumentData*> _data;
const Lottie::ColorReplacements *_replacements = nullptr; const Lottie::ColorReplacements *_replacements = nullptr;
std::unique_ptr<Lottie::SinglePlayer> _lottie; std::unique_ptr<Lottie::SinglePlayer> _lottie;
mutable std::shared_ptr<Data::DocumentMedia> _dataMedia;
ClickHandlerPtr _link; ClickHandlerPtr _link;
QSize _size; QSize _size;
QImage _lastDiceFrame; QImage _lastDiceFrame;

View File

@ -12,6 +12,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "history/view/history_view_element.h" #include "history/view/history_view_element.h"
#include "history/view/history_view_cursor_state.h" #include "history/view/history_view_cursor_state.h"
#include "data/data_document.h" #include "data/data_document.h"
#include "data/data_document_media.h"
#include "data/data_file_origin.h" #include "data/data_file_origin.h"
#include "base/qthelp_url.h" #include "base/qthelp_url.h"
#include "window/themes/window_theme.h" #include "window/themes/window_theme.h"
@ -181,8 +182,11 @@ void ThemeDocument::validateThumbnail() const {
if (_thumbnailGood > 0) { if (_thumbnailGood > 0) {
return; return;
} }
const auto good = _data->goodThumbnail(); if (!_dataMedia) {
if (good) { _dataMedia = _data->createMediaView();
_dataMedia->goodThumbnailWanted();
}
if (const auto good = _dataMedia->goodThumbnail()) {
if (good->loaded()) { if (good->loaded()) {
prepareThumbnailFrom(good, 1); prepareThumbnailFrom(good, 1);
return; return;

View File

@ -9,9 +9,13 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "history/view/media/history_view_file.h" #include "history/view/media/history_view_file.h"
namespace Data {
class DocumentMedia;
} // namespace Data
namespace HistoryView { namespace HistoryView {
class ThemeDocument : public File { class ThemeDocument final : public File {
public: public:
ThemeDocument( ThemeDocument(
not_null<Element*> parent, not_null<Element*> parent,
@ -54,11 +58,12 @@ private:
void validateThumbnail() const; void validateThumbnail() const;
void prepareThumbnailFrom(not_null<Image*> image, int good) const; void prepareThumbnailFrom(not_null<Image*> image, int good) const;
not_null<DocumentData*> _data; const not_null<DocumentData*> _data;
int _pixw = 1; int _pixw = 1;
int _pixh = 1; int _pixh = 1;
mutable QPixmap _thumbnail; mutable QPixmap _thumbnail;
mutable int _thumbnailGood = -1; // -1 inline, 0 thumbnail, 1 good mutable int _thumbnailGood = -1; // -1 inline, 0 thumbnail, 1 good
mutable std::shared_ptr<Data::DocumentMedia> _dataMedia;
// For wallpaper documents. // For wallpaper documents.
QColor _background; QColor _background;

View File

@ -10,7 +10,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "media/streaming/media_streaming_instance.h" #include "media/streaming/media_streaming_instance.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_file_origin.h" #include "data/data_file_origin.h"
#include "main/main_session.h"
#include "storage/file_download.h" // Storage::kMaxFileInMemory. #include "storage/file_download.h" // Storage::kMaxFileInMemory.
#include "styles/style_widgets.h" #include "styles/style_widgets.h"
@ -192,48 +194,53 @@ void Document::waitingChange(bool waiting) {
} }
void Document::validateGoodThumbnail() { void Document::validateGoodThumbnail() {
const auto good = _document->goodThumbnail(); if (_info.video.cover.isNull() || _document->goodThumbnailChecked()) {
if (_info.video.cover.isNull()
|| (good && good->loaded())
|| _document->uploading()) {
return; return;
} }
auto image = [&] { const auto document = _document;
auto result = _info.video.cover; const auto information = _info.video;
if (_info.video.rotation != 0) { const auto key = document->goodThumbnailCacheKey();
auto transform = QTransform(); const auto guard = base::make_weak(&document->session());
transform.rotate(_info.video.rotation); document->owner().cache().get(key, [=](QByteArray value) {
result = result.transformed(transform); if (!value.isEmpty()) {
return;
} }
if (result.size() != _info.video.size) { const auto image = [&] {
result = result.scaled( auto result = information.cover;
_info.video.size, if (information.rotation != 0) {
Qt::IgnoreAspectRatio, auto transform = QTransform();
Qt::SmoothTransformation); transform.rotate(information.rotation);
result = result.transformed(transform);
}
if (result.size() != information.size) {
result = result.scaled(
information.size,
Qt::IgnoreAspectRatio,
Qt::SmoothTransformation);
}
return result;
}();
auto bytes = QByteArray();
{
auto buffer = QBuffer(&bytes);
image.save(&buffer, "JPG", kGoodThumbnailQuality);
} }
return result; const auto length = bytes.size();
}(); if (!length || length > Storage::kMaxFileInMemory) {
LOG(("App Error: Bad thumbnail data for saving to cache."));
auto bytes = QByteArray(); bytes = "(failed)";
{ }
auto buffer = QBuffer(&bytes); crl::on_main(guard, [=] {
image.save(&buffer, "JPG", kGoodThumbnailQuality); if (const auto active = document->activeMediaView()) {
} active->setGoodThumbnail(image);
const auto length = bytes.size(); }
if (!length || length > Storage::kMaxFileInMemory) { document->owner().cache().putIfEmpty(
LOG(("App Error: Bad thumbnail data for saving to cache.")); document->goodThumbnailCacheKey(),
} else if (_document->uploading()) { Storage::Cache::Database::TaggedValue(
_document->setGoodThumbnailOnUpload( base::duplicate(bytes),
std::move(image), Data::kImageCacheTag));
std::move(bytes)); });
} else { });
_document->owner().cache().putIfEmpty(
_document->goodThumbnailCacheKey(),
Storage::Cache::Database::TaggedValue(
std::move(bytes),
Data::kImageCacheTag));
_document->refreshGoodThumbnail();
}
} }
void Document::waitingCallback() { void Document::waitingCallback() {

View File

@ -40,6 +40,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "data/data_user.h" #include "data/data_user.h"
#include "data/data_file_origin.h" #include "data/data_file_origin.h"
#include "data/data_media_rotation.h" #include "data/data_media_rotation.h"
#include "data/data_document_media.h"
#include "window/themes/window_theme_preview.h" #include "window/themes/window_theme_preview.h"
#include "window/window_peer_menu.h" #include "window/window_peer_menu.h"
#include "window/window_session_controller.h" #include "window/window_session_controller.h"
@ -2177,7 +2178,8 @@ void OverlayWidget::startStreamingPlayer() {
void OverlayWidget::initStreamingThumbnail() { void OverlayWidget::initStreamingThumbnail() {
Expects(_doc != nullptr); Expects(_doc != nullptr);
const auto good = _doc->goodThumbnail(); const auto media = _doc->activeMediaView();
const auto good = media ? media->goodThumbnail() : nullptr;
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());

View File

@ -348,6 +348,7 @@ private:
PhotoData *_photo = nullptr; PhotoData *_photo = nullptr;
DocumentData *_doc = nullptr; DocumentData *_doc = nullptr;
std::shared_ptr<Data::DocumentMedia> _docMedia;
int _rotation = 0; int _rotation = 0;
std::unique_ptr<SharedMedia> _sharedMedia; std::unique_ptr<SharedMedia> _sharedMedia;
std::optional<SharedMediaWithLastSlice> _sharedMediaData; std::optional<SharedMediaWithLastSlice> _sharedMediaData;

View File

@ -13,6 +13,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "media/view/media_view_playback_progress.h" #include "media/view/media_view_playback_progress.h"
#include "media/audio/media_audio.h" #include "media/audio/media_audio.h"
#include "data/data_document.h" #include "data/data_document.h"
#include "data/data_document_media.h"
#include "data/data_file_origin.h" #include "data/data_file_origin.h"
#include "data/data_session.h" #include "data/data_session.h"
#include "data/data_media_rotation.h" #include "data/data_media_rotation.h"
@ -806,20 +807,20 @@ void PipPanel::updateDecorations() {
Pip::Pip( Pip::Pip(
not_null<Delegate*> delegate, not_null<Delegate*> delegate,
not_null<DocumentData*> document, not_null<DocumentData*> data,
FullMsgId contextId, FullMsgId contextId,
std::shared_ptr<Streaming::Document> shared, std::shared_ptr<Streaming::Document> shared,
FnMut<void()> closeAndContinue, FnMut<void()> closeAndContinue,
FnMut<void()> destroy) FnMut<void()> destroy)
: _delegate(delegate) : _delegate(delegate)
, _document(document) , _data(data)
, _contextId(contextId) , _contextId(contextId)
, _instance(std::move(shared), [=] { waitingAnimationCallback(); }) , _instance(std::move(shared), [=] { waitingAnimationCallback(); })
, _panel( , _panel(
_delegate->pipParentWidget(), _delegate->pipParentWidget(),
[=](QPainter &p, const FrameRequest &request) { paint(p, request); }) [=](QPainter &p, const FrameRequest &request) { paint(p, request); })
, _playbackProgress(std::make_unique<PlaybackProgress>()) , _playbackProgress(std::make_unique<PlaybackProgress>())
, _rotation(document->owner().mediaRotation().get(document)) , _rotation(data->owner().mediaRotation().get(data))
, _roundRect(ImageRoundRadius::Large, st::radialBg) , _roundRect(ImageRoundRadius::Large, st::radialBg)
, _closeAndContinue(std::move(closeAndContinue)) , _closeAndContinue(std::move(closeAndContinue))
, _destroy(std::move(destroy)) { , _destroy(std::move(destroy)) {
@ -835,9 +836,10 @@ void Pip::setupPanel() {
if (!_instance.info().video.size.isEmpty()) { if (!_instance.info().video.size.isEmpty()) {
return _instance.info().video.size; return _instance.info().video.size;
} }
const auto good = _document->goodThumbnail(); const auto media = _data->activeMediaView();
const auto good = media ? media->goodThumbnail() : nullptr;
const auto useGood = (good && good->loaded()); const auto useGood = (good && good->loaded());
const auto original = useGood ? good->size() : _document->dimensions; const auto original = useGood ? good->size() : _data->dimensions;
return original.isEmpty() ? QSize(1, 1) : original; return original.isEmpty() ? QSize(1, 1) : original;
}(); }();
_panel.setAspectRatio(FlipSizeByRotation(size, _rotation)); _panel.setAspectRatio(FlipSizeByRotation(size, _rotation));
@ -1369,11 +1371,12 @@ QImage Pip::videoFrame(const FrameRequest &request) const {
return _instance.frame(request); return _instance.frame(request);
} }
const auto &cover = _instance.info().video.cover; const auto &cover = _instance.info().video.cover;
const auto good = _document->goodThumbnail(); const auto media = _data->activeMediaView();
const auto good = media ? media->goodThumbnail() : nullptr;
const auto useGood = (good && good->loaded()); const auto useGood = (good && good->loaded());
const auto thumb = _document->thumbnail(); const auto thumb = _data->thumbnail();
const auto useThumb = (thumb && thumb->loaded()); const auto useThumb = (thumb && thumb->loaded());
const auto blurred = _document->thumbnailInline(); const auto blurred = _data->thumbnailInline();
const auto state = !cover.isNull() const auto state = !cover.isNull()
? ThumbState::Cover ? ThumbState::Cover
: useGood : useGood

View File

@ -14,6 +14,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include <QtCore/QPointer> #include <QtCore/QPointer>
namespace Data {
class DocumentMedia;
} // namespace Data
namespace Ui { namespace Ui {
class IconButton; class IconButton;
template <typename Widget> template <typename Widget>
@ -121,7 +125,7 @@ public:
Pip( Pip(
not_null<Delegate*> delegate, not_null<Delegate*> delegate,
not_null<DocumentData*> document, not_null<DocumentData*> data,
FullMsgId contextId, FullMsgId contextId,
std::shared_ptr<Streaming::Document> shared, std::shared_ptr<Streaming::Document> shared,
FnMut<void()> closeAndContinue, FnMut<void()> closeAndContinue,
@ -197,12 +201,13 @@ private:
void seekFinish(float64 value); void seekFinish(float64 value);
const not_null<Delegate*> _delegate; const not_null<Delegate*> _delegate;
not_null<DocumentData*> _document; not_null<DocumentData*> _data;
FullMsgId _contextId; FullMsgId _contextId;
Streaming::Instance _instance; Streaming::Instance _instance;
PipPanel _panel; PipPanel _panel;
QSize _size; QSize _size;
std::unique_ptr<PlaybackProgress> _playbackProgress; std::unique_ptr<PlaybackProgress> _playbackProgress;
std::shared_ptr<Data::DocumentMedia> _dataMedia;
bool _showPause = false; bool _showPause = false;
bool _startPaused = false; bool _startPaused = false;

View File

@ -13,6 +13,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "data/data_media_types.h" #include "data/data_media_types.h"
#include "data/data_peer.h" #include "data/data_peer.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 "core/file_utilities.h" #include "core/file_utilities.h"
@ -417,13 +418,10 @@ Video::Video(
, _duration(formatDurationText(_data->getDuration())) { , _duration(formatDurationText(_data->getDuration())) {
setDocumentLinks(_data); setDocumentLinks(_data);
_data->loadThumbnail(parent->fullId()); _data->loadThumbnail(parent->fullId());
if (_data->hasThumbnail() && !_data->thumbnail()->loaded()) {
if (const auto good = _data->goodThumbnail()) {
good->load({});
}
}
} }
Video::~Video() = default;
void Video::initDimensions() { void Video::initDimensions() {
_maxw = 2 * st::overviewPhotoMinSize; _maxw = 2 * st::overviewPhotoMinSize;
_minh = _maxw; _minh = _maxw;
@ -436,12 +434,14 @@ int32 Video::resizeGetHeight(int32 width) {
} }
void Video::paint(Painter &p, const QRect &clip, TextSelection selection, const PaintContext *context) { void Video::paint(Painter &p, const QRect &clip, TextSelection selection, const PaintContext *context) {
ensureDataMediaCreated();
const auto selected = (selection == FullSelection); const auto selected = (selection == FullSelection);
const auto blurred = _data->thumbnailInline(); const auto blurred = _data->thumbnailInline();
const auto goodLoaded = _data->goodThumbnail()
&& _data->goodThumbnail()->loaded();
const auto thumbLoaded = _data->hasThumbnail() const auto thumbLoaded = _data->hasThumbnail()
&& _data->thumbnail()->loaded(); && _data->thumbnail()->loaded();
const auto goodLoaded = _dataMedia->goodThumbnail()
&& _dataMedia->goodThumbnail()->loaded();
bool loaded = _data->loaded(), displayLoading = _data->displayLoading(); bool loaded = _data->loaded(), displayLoading = _data->displayLoading();
if (displayLoading) { if (displayLoading) {
@ -459,7 +459,7 @@ void Video::paint(Painter &p, const QRect &clip, TextSelection selection, const
|| (_pixBlurred && (thumbLoaded || goodLoaded)))) { || (_pixBlurred && (thumbLoaded || goodLoaded)))) {
auto size = _width * cIntRetinaFactor(); auto size = _width * cIntRetinaFactor();
auto img = goodLoaded auto img = goodLoaded
? _data->goodThumbnail()->original() ? _dataMedia->goodThumbnail()->original()
: thumbLoaded : thumbLoaded
? _data->thumbnail()->original() ? _data->thumbnail()->original()
: Images::prepareBlur(blurred->original()); : Images::prepareBlur(blurred->original());
@ -543,6 +543,14 @@ void Video::paint(Painter &p, const QRect &clip, TextSelection selection, const
paintCheckbox(p, { checkLeft, checkTop }, selected, context); paintCheckbox(p, { checkLeft, checkTop }, selected, context);
} }
void Video::ensureDataMediaCreated() const {
if (_dataMedia) {
return;
}
_dataMedia = _data->createMediaView();
_dataMedia->goodThumbnailWanted();
}
float64 Video::dataProgress() const { float64 Video::dataProgress() const {
return _data->progress(); return _data->progress();
} }

View File

@ -19,6 +19,7 @@ struct RoundCheckbox;
namespace Data { namespace Data {
class Media; class Media;
class DocumentMedia;
} // namespace Data } // namespace Data
namespace Overview { namespace Overview {
@ -220,11 +221,12 @@ private:
}; };
class Video : public RadialProgressItem { class Video final : public RadialProgressItem {
public: public:
Video( Video(
not_null<HistoryItem*> parent, not_null<HistoryItem*> parent,
not_null<DocumentData*> video); not_null<DocumentData*> video);
~Video();
void initDimensions() override; void initDimensions() override;
int32 resizeGetHeight(int32 width) override; int32 resizeGetHeight(int32 width) override;
@ -240,15 +242,17 @@ protected:
bool iconAnimated() const override; bool iconAnimated() const override;
private: private:
void ensureDataMediaCreated() const;
void updateStatusText();
not_null<DocumentData*> _data; not_null<DocumentData*> _data;
mutable std::shared_ptr<Data::DocumentMedia> _dataMedia;
StatusText _status; StatusText _status;
QString _duration; QString _duration;
QPixmap _pix; QPixmap _pix;
bool _pixBlurred = true; bool _pixBlurred = true;
void updateStatusText();
}; };
class Voice : public RadialProgressItem { class Voice : public RadialProgressItem {

View File

@ -10,6 +10,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "storage/localimageloader.h" #include "storage/localimageloader.h"
#include "storage/file_download.h" #include "storage/file_download.h"
#include "data/data_document.h" #include "data/data_document.h"
#include "data/data_document_media.h"
#include "data/data_photo.h" #include "data/data_photo.h"
#include "data/data_session.h" #include "data/data_session.h"
#include "main/main_session.h" #include "main/main_session.h"
@ -193,9 +194,18 @@ void Uploader::upload(
std::move(file->thumb)); std::move(file->thumb));
document->uploadingData = std::make_unique<Data::UploadState>( document->uploadingData = std::make_unique<Data::UploadState>(
document->size); document->size);
document->setGoodThumbnailOnUpload( if (!file->goodThumbnail.isNull()) {
std::move(file->goodThumbnail), if (const auto active = document->activeMediaView()) {
std::move(file->goodThumbnailBytes)); active->setGoodThumbnail(std::move(file->goodThumbnail));
}
}
if (!file->goodThumbnailBytes.isEmpty()) {
document->owner().cache().putIfEmpty(
document->goodThumbnailCacheKey(),
Storage::Cache::Database::TaggedValue(
std::move(file->goodThumbnailBytes),
Data::kImageCacheTag));
}
if (!file->content.isEmpty()) { if (!file->content.isEmpty()) {
document->setDataAndCache(file->content); document->setDataAndCache(file->content);
if (file->type == SendMediaType::ThemeFile) { if (file->type == SendMediaType::ThemeFile) {