mirror of
https://github.com/kotatogram/kotatogram-desktop
synced 2025-08-31 06:35:14 +00:00
Move photo data to Data::PhotoMedia.
This commit is contained in:
@@ -10,8 +10,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "data/data_peer.h"
|
||||
#include "data/data_photo.h"
|
||||
#include "data/data_document.h"
|
||||
#include "ui/image/image_source.h"
|
||||
#include "ui/image/image.h"
|
||||
|
||||
#include <QtCore/QBuffer>
|
||||
|
||||
@@ -294,11 +292,11 @@ bool Should(
|
||||
bool Should(
|
||||
const Full &data,
|
||||
not_null<PeerData*> peer,
|
||||
not_null<Images::Source*> image) {
|
||||
not_null<PhotoData*> photo) {
|
||||
return data.shouldDownload(
|
||||
SourceFromPeer(peer),
|
||||
Type::Photo,
|
||||
image->bytesSize());
|
||||
photo->imageByteSize(PhotoSize::Large));
|
||||
}
|
||||
|
||||
bool ShouldAutoPlay(
|
||||
|
@@ -9,10 +9,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
|
||||
#include <array>
|
||||
|
||||
namespace Images {
|
||||
class Source;
|
||||
} // namespace Images
|
||||
|
||||
namespace Data {
|
||||
namespace AutoDownload {
|
||||
|
||||
@@ -118,7 +114,7 @@ private:
|
||||
[[nodiscard]] bool Should(
|
||||
const Full &data,
|
||||
not_null<PeerData*> peer,
|
||||
not_null<Images::Source*> image);
|
||||
not_null<PhotoData*> photo);
|
||||
|
||||
[[nodiscard]] bool ShouldAutoPlay(
|
||||
const Full &data,
|
||||
|
@@ -700,10 +700,10 @@ void DocumentData::loadThumbnail(Data::FileOrigin origin) {
|
||||
_flags |= Flag::ThumbnailFailed;
|
||||
}, [=] {
|
||||
if (_thumbnailLoader && !_thumbnailLoader->cancelled()) {
|
||||
if (auto image = _thumbnailLoader->imageData(); image.isNull()) {
|
||||
if (auto read = _thumbnailLoader->imageData(); read.isNull()) {
|
||||
_flags |= Flag::ThumbnailFailed;
|
||||
} else if (const auto active = activeMediaView()) {
|
||||
active->setThumbnail(std::move(image));
|
||||
active->setThumbnail(std::move(read));
|
||||
}
|
||||
}
|
||||
_thumbnailLoader = nullptr;
|
||||
|
@@ -159,8 +159,8 @@ public:
|
||||
[[nodiscard]] bool thumbnailLoading() const;
|
||||
[[nodiscard]] bool thumbnailFailed() const;
|
||||
void loadThumbnail(Data::FileOrigin origin);
|
||||
const ImageLocation &thumbnailLocation() const;
|
||||
int thumbnailByteSize() const;
|
||||
[[nodiscard]] const ImageLocation &thumbnailLocation() const;
|
||||
[[nodiscard]] int thumbnailByteSize() const;
|
||||
|
||||
[[nodiscard]] bool hasVideoThumbnail() const;
|
||||
[[nodiscard]] bool videoThumbnailLoading() const;
|
||||
|
@@ -381,98 +381,98 @@ bool MediaPhoto::updateSentMedia(const MTPMessageMedia &media) {
|
||||
}
|
||||
parent()->history()->owner().photoConvert(_photo, *content);
|
||||
|
||||
if (content->type() != mtpc_photo) {
|
||||
return false;
|
||||
}
|
||||
const auto &photo = content->c_photo();
|
||||
//if (content->type() != mtpc_photo) { // #TODO optimize
|
||||
// return false;
|
||||
//}
|
||||
//const auto &photo = content->c_photo();
|
||||
|
||||
struct SizeData {
|
||||
MTPstring type = MTP_string();
|
||||
int width = 0;
|
||||
int height = 0;
|
||||
QByteArray bytes;
|
||||
};
|
||||
const auto saveImageToCache = [&](
|
||||
not_null<Image*> image,
|
||||
SizeData size) {
|
||||
Expects(!size.type.v.isEmpty());
|
||||
//struct SizeData {
|
||||
// MTPstring type = MTP_string();
|
||||
// int width = 0;
|
||||
// int height = 0;
|
||||
// QByteArray bytes;
|
||||
//};
|
||||
//const auto saveImageToCache = [&](
|
||||
// not_null<Image*> image,
|
||||
// SizeData size) {
|
||||
// Expects(!size.type.v.isEmpty());
|
||||
|
||||
const auto key = StorageImageLocation(
|
||||
StorageFileLocation(
|
||||
photo.vdc_id().v,
|
||||
_photo->session().userId(),
|
||||
MTP_inputPhotoFileLocation(
|
||||
photo.vid(),
|
||||
photo.vaccess_hash(),
|
||||
photo.vfile_reference(),
|
||||
size.type)),
|
||||
size.width,
|
||||
size.height);
|
||||
if (!key.valid() || image->isNull() || !image->loaded()) {
|
||||
return;
|
||||
}
|
||||
if (size.bytes.isEmpty()) {
|
||||
size.bytes = image->bytesForCache();
|
||||
}
|
||||
const auto length = size.bytes.size();
|
||||
if (!length || length > Storage::kMaxFileInMemory) {
|
||||
LOG(("App Error: Bad photo data for saving to cache."));
|
||||
return;
|
||||
}
|
||||
parent()->history()->owner().cache().putIfEmpty(
|
||||
key.file().cacheKey(),
|
||||
Storage::Cache::Database::TaggedValue(
|
||||
std::move(size.bytes),
|
||||
Data::kImageCacheTag));
|
||||
image->replaceSource(
|
||||
std::make_unique<Images::StorageSource>(key, length));
|
||||
};
|
||||
auto &sizes = photo.vsizes().v;
|
||||
auto max = 0;
|
||||
auto maxSize = SizeData();
|
||||
for (const auto &data : sizes) {
|
||||
const auto size = data.match([](const MTPDphotoSize &data) {
|
||||
return SizeData{
|
||||
data.vtype(),
|
||||
data.vw().v,
|
||||
data.vh().v,
|
||||
QByteArray()
|
||||
};
|
||||
}, [](const MTPDphotoCachedSize &data) {
|
||||
return SizeData{
|
||||
data.vtype(),
|
||||
data.vw().v,
|
||||
data.vh().v,
|
||||
qba(data.vbytes())
|
||||
};
|
||||
}, [](const MTPDphotoSizeEmpty &) {
|
||||
return SizeData();
|
||||
}, [](const MTPDphotoStrippedSize &data) {
|
||||
// No need to save stripped images to local cache.
|
||||
return SizeData();
|
||||
});
|
||||
const auto letter = size.type.v.isEmpty() ? char(0) : size.type.v[0];
|
||||
if (!letter) {
|
||||
continue;
|
||||
}
|
||||
if (letter == 's') {
|
||||
saveImageToCache(_photo->thumbnailSmall(), size);
|
||||
} else if (letter == 'm') {
|
||||
saveImageToCache(_photo->thumbnail(), size);
|
||||
} else if (letter == 'x' && max < 1) {
|
||||
max = 1;
|
||||
maxSize = size;
|
||||
} else if (letter == 'y' && max < 2) {
|
||||
max = 2;
|
||||
maxSize = size;
|
||||
//} else if (letter == 'w' && max < 3) {
|
||||
// max = 3;
|
||||
// maxSize = size;
|
||||
}
|
||||
}
|
||||
if (!maxSize.type.v.isEmpty()) {
|
||||
saveImageToCache(_photo->large(), maxSize);
|
||||
}
|
||||
// const auto key = StorageImageLocation(
|
||||
// StorageFileLocation(
|
||||
// photo.vdc_id().v,
|
||||
// _photo->session().userId(),
|
||||
// MTP_inputPhotoFileLocation(
|
||||
// photo.vid(),
|
||||
// photo.vaccess_hash(),
|
||||
// photo.vfile_reference(),
|
||||
// size.type)),
|
||||
// size.width,
|
||||
// size.height);
|
||||
// if (!key.valid() || image->isNull() || !image->loaded()) {
|
||||
// return;
|
||||
// }
|
||||
// if (size.bytes.isEmpty()) {
|
||||
// size.bytes = image->bytesForCache();
|
||||
// }
|
||||
// const auto length = size.bytes.size();
|
||||
// if (!length || length > Storage::kMaxFileInMemory) {
|
||||
// LOG(("App Error: Bad photo data for saving to cache."));
|
||||
// return;
|
||||
// }
|
||||
// parent()->history()->owner().cache().putIfEmpty(
|
||||
// key.file().cacheKey(),
|
||||
// Storage::Cache::Database::TaggedValue(
|
||||
// std::move(size.bytes),
|
||||
// Data::kImageCacheTag));
|
||||
// image->replaceSource(
|
||||
// std::make_unique<Images::StorageSource>(key, length));
|
||||
//};
|
||||
//auto &sizes = photo.vsizes().v;
|
||||
//auto max = 0;
|
||||
//auto maxSize = SizeData();
|
||||
//for (const auto &data : sizes) {
|
||||
// const auto size = data.match([](const MTPDphotoSize &data) {
|
||||
// return SizeData{
|
||||
// data.vtype(),
|
||||
// data.vw().v,
|
||||
// data.vh().v,
|
||||
// QByteArray()
|
||||
// };
|
||||
// }, [](const MTPDphotoCachedSize &data) {
|
||||
// return SizeData{
|
||||
// data.vtype(),
|
||||
// data.vw().v,
|
||||
// data.vh().v,
|
||||
// qba(data.vbytes())
|
||||
// };
|
||||
// }, [](const MTPDphotoSizeEmpty &) {
|
||||
// return SizeData();
|
||||
// }, [](const MTPDphotoStrippedSize &data) {
|
||||
// // No need to save stripped images to local cache.
|
||||
// return SizeData();
|
||||
// });
|
||||
// const auto letter = size.type.v.isEmpty() ? char(0) : size.type.v[0];
|
||||
// if (!letter) {
|
||||
// continue;
|
||||
// }
|
||||
// if (letter == 's') {
|
||||
// saveImageToCache(_photo->thumbnailSmall(), size);
|
||||
// } else if (letter == 'm') {
|
||||
// saveImageToCache(_photo->thumbnail(), size);
|
||||
// } else if (letter == 'x' && max < 1) {
|
||||
// max = 1;
|
||||
// maxSize = size;
|
||||
// } else if (letter == 'y' && max < 2) {
|
||||
// max = 2;
|
||||
// maxSize = size;
|
||||
// //} else if (letter == 'w' && max < 3) {
|
||||
// // max = 3;
|
||||
// // maxSize = size;
|
||||
// }
|
||||
//}
|
||||
//if (!maxSize.type.v.isEmpty()) {
|
||||
// saveImageToCache(_photo->large(), maxSize);
|
||||
//}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@@ -10,20 +10,35 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "data/data_session.h"
|
||||
#include "data/data_file_origin.h"
|
||||
#include "data/data_reply_preview.h"
|
||||
#include "data/data_photo_media.h"
|
||||
#include "ui/image/image.h"
|
||||
#include "ui/image/image_source.h"
|
||||
#include "main/main_session.h"
|
||||
#include "mainwidget.h"
|
||||
#include "storage/file_download.h"
|
||||
#include "core/application.h"
|
||||
#include "facades.h"
|
||||
#include "app.h"
|
||||
|
||||
namespace {
|
||||
|
||||
using Data::PhotoMedia;
|
||||
using Data::PhotoSize;
|
||||
using Data::PhotoSizeIndex;
|
||||
using Data::kPhotoSizeCount;
|
||||
|
||||
} // namespace
|
||||
|
||||
PhotoData::PhotoData(not_null<Data::Session*> owner, PhotoId id)
|
||||
: id(id)
|
||||
, _owner(owner) {
|
||||
}
|
||||
|
||||
PhotoData::~PhotoData() = default;
|
||||
PhotoData::~PhotoData() {
|
||||
for (auto &image : _images) {
|
||||
base::take(image.loader).reset();
|
||||
}
|
||||
}
|
||||
|
||||
Data::Session &PhotoData::owner() const {
|
||||
return *_owner;
|
||||
@@ -33,55 +48,77 @@ Main::Session &PhotoData::session() const {
|
||||
return _owner->session();
|
||||
}
|
||||
|
||||
void PhotoData::automaticLoad(
|
||||
Data::FileOrigin origin,
|
||||
const HistoryItem *item) {
|
||||
_large->automaticLoad(origin, item);
|
||||
}
|
||||
|
||||
void PhotoData::automaticLoadSettingsChanged() {
|
||||
_large->automaticLoadSettingsChanged();
|
||||
}
|
||||
|
||||
void PhotoData::download(Data::FileOrigin origin) {
|
||||
_large->loadEvenCancelled(origin);
|
||||
_owner->notifyPhotoLayoutChanged(this);
|
||||
}
|
||||
|
||||
bool PhotoData::loaded() const {
|
||||
bool wasLoading = loading();
|
||||
if (_large->loaded()) {
|
||||
if (wasLoading) {
|
||||
_owner->notifyPhotoLayoutChanged(this);
|
||||
}
|
||||
return true;
|
||||
const auto index = PhotoSizeIndex(PhotoSize::Large);
|
||||
if (!(_images[index].flags & ImageFlag::Cancelled)) {
|
||||
return;
|
||||
}
|
||||
return false;
|
||||
_images[index].loader = nullptr;
|
||||
_images[index].flags &= ~ImageFlag::Cancelled;
|
||||
}
|
||||
|
||||
void PhotoData::load(
|
||||
Data::FileOrigin origin,
|
||||
LoadFromCloudSetting fromCloud,
|
||||
bool autoLoading) {
|
||||
load(PhotoSize::Large, origin, fromCloud, autoLoading);
|
||||
}
|
||||
|
||||
bool PhotoData::loading() const {
|
||||
return _large->loading();
|
||||
return loading(PhotoSize::Large);
|
||||
}
|
||||
|
||||
bool PhotoData::loading(PhotoSize size) const {
|
||||
return (_images[PhotoSizeIndex(size)].loader != nullptr);
|
||||
}
|
||||
|
||||
bool PhotoData::failed(PhotoSize size) const {
|
||||
return (_images[PhotoSizeIndex(size)].flags & ImageFlag::Failed);
|
||||
}
|
||||
|
||||
const ImageLocation &PhotoData::location(PhotoSize size) const {
|
||||
return _images[PhotoSizeIndex(size)].location;
|
||||
}
|
||||
|
||||
int PhotoData::imageByteSize(PhotoSize size) const {
|
||||
return _images[PhotoSizeIndex(size)].byteSize;
|
||||
}
|
||||
|
||||
bool PhotoData::displayLoading() const {
|
||||
return _large->loading()
|
||||
? _large->displayLoading()
|
||||
const auto index = PhotoSizeIndex(PhotoSize::Large);
|
||||
return _images[index].loader
|
||||
? (!_images[index].loader->loadingLocal()
|
||||
|| !_images[index].loader->autoLoading())
|
||||
: (uploading() && !waitingForAlbum());
|
||||
}
|
||||
|
||||
void PhotoData::cancel() {
|
||||
_large->cancel();
|
||||
_owner->notifyPhotoLayoutChanged(this);
|
||||
if (!loading()) {
|
||||
return;
|
||||
}
|
||||
|
||||
const auto index = PhotoSizeIndex(PhotoSize::Large);
|
||||
_images[index].flags |= ImageFlag::Cancelled;
|
||||
destroyLoader(PhotoSize::Large);
|
||||
_owner->photoLoadDone(this);
|
||||
}
|
||||
|
||||
float64 PhotoData::progress() const {
|
||||
if (uploading()) {
|
||||
if (uploadingData->size > 0) {
|
||||
return float64(uploadingData->offset) / uploadingData->size;
|
||||
const auto result = float64(uploadingData->offset)
|
||||
/ uploadingData->size;
|
||||
return snap(result, 0., 1.);
|
||||
}
|
||||
return 0;
|
||||
return 0.;
|
||||
}
|
||||
return _large->progress();
|
||||
const auto index = PhotoSizeIndex(PhotoSize::Large);
|
||||
return loading() ? _images[index].loader->currentProgress() : 0.;
|
||||
}
|
||||
|
||||
bool PhotoData::cancelled() const {
|
||||
const auto index = PhotoSizeIndex(PhotoSize::Large);
|
||||
return (_images[index].flags & ImageFlag::Cancelled);
|
||||
}
|
||||
|
||||
void PhotoData::setWaitingForAlbum() {
|
||||
@@ -95,7 +132,8 @@ bool PhotoData::waitingForAlbum() const {
|
||||
}
|
||||
|
||||
int32 PhotoData::loadOffset() const {
|
||||
return _large->loadOffset();
|
||||
const auto index = PhotoSizeIndex(PhotoSize::Large);
|
||||
return loading() ? _images[index].loader->currentOffset() : 0;
|
||||
}
|
||||
|
||||
bool PhotoData::uploading() const {
|
||||
@@ -103,11 +141,6 @@ bool PhotoData::uploading() const {
|
||||
}
|
||||
|
||||
void PhotoData::unload() {
|
||||
// Forget thumbnail only when image cache limit exceeds.
|
||||
//_thumbnailInline->unload();
|
||||
_thumbnailSmall->unload();
|
||||
_thumbnail->unload();
|
||||
_large->unload();
|
||||
_replyPreview = nullptr;
|
||||
}
|
||||
|
||||
@@ -142,9 +175,9 @@ QByteArray PhotoData::fileReference() const {
|
||||
|
||||
void PhotoData::refreshFileReference(const QByteArray &value) {
|
||||
_fileReference = value;
|
||||
_thumbnailSmall->refreshFileReference(value);
|
||||
_thumbnail->refreshFileReference(value);
|
||||
_large->refreshFileReference(value);
|
||||
for (auto &image : _images) {
|
||||
image.location.refreshFileReference(value);
|
||||
}
|
||||
}
|
||||
|
||||
void PhotoData::collectLocalData(not_null<PhotoData*> local) {
|
||||
@@ -152,83 +185,185 @@ void PhotoData::collectLocalData(not_null<PhotoData*> local) {
|
||||
return;
|
||||
}
|
||||
|
||||
const auto copyImage = [&](const ImagePtr &src, const ImagePtr &dst) {
|
||||
if (const auto from = src->cacheKey()) {
|
||||
if (const auto to = dst->cacheKey()) {
|
||||
for (auto i = 0; i != kPhotoSizeCount; ++i) {
|
||||
if (const auto from = local->_images[i].location.file().cacheKey()) {
|
||||
if (const auto to = _images[i].location.file().cacheKey()) {
|
||||
_owner->cache().copyIfEmpty(from, to);
|
||||
}
|
||||
}
|
||||
};
|
||||
copyImage(local->_thumbnailSmall, _thumbnailSmall);
|
||||
copyImage(local->_thumbnail, _thumbnail);
|
||||
copyImage(local->_large, _large);
|
||||
}
|
||||
if (const auto localMedia = local->activeMediaView()) {
|
||||
const auto media = createMediaView();
|
||||
media->collectLocalData(localMedia.get());
|
||||
|
||||
// Keep DocumentMedia alive for some more time.
|
||||
// NB! This allows DocumentMedia to outlive Main::Session!
|
||||
// In case this is a problem this code should be rewritten.
|
||||
crl::on_main(&session(), [media] {});
|
||||
}
|
||||
}
|
||||
|
||||
bool PhotoData::isNull() const {
|
||||
return _large->isNull();
|
||||
return !_images[PhotoSizeIndex(PhotoSize::Large)].location.valid();
|
||||
}
|
||||
|
||||
void PhotoData::loadThumbnail(Data::FileOrigin origin) {
|
||||
_thumbnail->load(origin);
|
||||
void PhotoData::load(
|
||||
PhotoSize size,
|
||||
Data::FileOrigin origin,
|
||||
LoadFromCloudSetting fromCloud,
|
||||
bool autoLoading) {
|
||||
const auto index = PhotoSizeIndex(size);
|
||||
auto &image = _images[index];
|
||||
if (image.loader) {
|
||||
if (fromCloud == LoadFromCloudOrLocal) {
|
||||
image.loader->permitLoadFromCloud();
|
||||
}
|
||||
return;
|
||||
} else if ((image.flags & ImageFlag::Failed)
|
||||
|| !image.location.valid()) {
|
||||
return;
|
||||
} else if (const auto active = activeMediaView()) {
|
||||
if (active->image(size)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
image.flags &= ~ImageFlag::Cancelled;
|
||||
image.loader = CreateFileLoader(
|
||||
image.location.file(),
|
||||
origin,
|
||||
QString(),
|
||||
image.byteSize,
|
||||
UnknownFileLocation,
|
||||
LoadToCacheAsWell,
|
||||
fromCloud,
|
||||
autoLoading,
|
||||
Data::kImageCacheTag);
|
||||
|
||||
image.loader->updates(
|
||||
) | rpl::start_with_next_error_done([=] {
|
||||
if (size == PhotoSize::Large) {
|
||||
_owner->photoLoadProgress(this);
|
||||
}
|
||||
}, [=, &image](bool started) {
|
||||
finishLoad(size);
|
||||
image.flags |= ImageFlag::Failed;
|
||||
if (size == PhotoSize::Large) {
|
||||
_owner->photoLoadFail(this, started);
|
||||
}
|
||||
}, [=, &image] {
|
||||
finishLoad(size);
|
||||
if (size == PhotoSize::Large) {
|
||||
_owner->photoLoadDone(this);
|
||||
}
|
||||
}, image.loader->lifetime());
|
||||
|
||||
image.loader->start();
|
||||
|
||||
if (size == PhotoSize::Large) {
|
||||
_owner->notifyPhotoLayoutChanged(this);
|
||||
}
|
||||
}
|
||||
|
||||
void PhotoData::loadThumbnailSmall(Data::FileOrigin origin) {
|
||||
_thumbnailSmall->load(origin);
|
||||
void PhotoData::finishLoad(PhotoSize size) {
|
||||
const auto index = PhotoSizeIndex(size);
|
||||
auto &image = _images[index];
|
||||
|
||||
// NB! image.loader may be in ~FileLoader() already.
|
||||
const auto guard = gsl::finally([&] {
|
||||
destroyLoader(size);
|
||||
});
|
||||
if (!image.loader || image.loader->cancelled()) {
|
||||
image.flags |= ImageFlag::Cancelled;
|
||||
return;
|
||||
} else if (auto read = image.loader->imageData(); read.isNull()) {
|
||||
image.flags |= ImageFlag::Failed;
|
||||
} else if (const auto active = activeMediaView()) {
|
||||
active->set(size, std::move(read));
|
||||
}
|
||||
}
|
||||
|
||||
Image *PhotoData::thumbnailInline() const {
|
||||
return _thumbnailInline ? _thumbnailInline.get() : nullptr;
|
||||
void PhotoData::destroyLoader(PhotoSize size) {
|
||||
const auto index = PhotoSizeIndex(size);
|
||||
auto &image = _images[index];
|
||||
|
||||
// NB! image.loader may be in ~FileLoader() already.
|
||||
if (!image.loader) {
|
||||
return;
|
||||
}
|
||||
const auto loader = base::take(image.loader);
|
||||
if (image.flags & ImageFlag::Cancelled) {
|
||||
loader->cancel();
|
||||
}
|
||||
}
|
||||
|
||||
not_null<Image*> PhotoData::thumbnailSmall() const {
|
||||
return _thumbnailSmall.get();
|
||||
std::shared_ptr<PhotoMedia> PhotoData::createMediaView() {
|
||||
if (auto result = activeMediaView()) {
|
||||
return result;
|
||||
}
|
||||
auto result = std::make_shared<PhotoMedia>(this);
|
||||
_media = result;
|
||||
return result;
|
||||
}
|
||||
|
||||
not_null<Image*> PhotoData::thumbnail() const {
|
||||
return _thumbnail.get();
|
||||
}
|
||||
|
||||
void PhotoData::load(Data::FileOrigin origin) {
|
||||
_large->load(origin);
|
||||
}
|
||||
|
||||
not_null<Image*> PhotoData::large() const {
|
||||
return _large.get();
|
||||
std::shared_ptr<PhotoMedia> PhotoData::activeMediaView() const {
|
||||
return _media.lock();
|
||||
}
|
||||
|
||||
void PhotoData::updateImages(
|
||||
ImagePtr thumbnailInline,
|
||||
ImagePtr thumbnailSmall,
|
||||
ImagePtr thumbnail,
|
||||
ImagePtr large) {
|
||||
if (!thumbnailSmall || !thumbnail || !large) {
|
||||
return;
|
||||
const QByteArray &inlineThumbnailBytes,
|
||||
const ImageWithLocation &small,
|
||||
const ImageWithLocation &thumbnail,
|
||||
const ImageWithLocation &large) {
|
||||
if (!inlineThumbnailBytes.isEmpty()
|
||||
&& _inlineThumbnailBytes.isEmpty()) {
|
||||
_inlineThumbnailBytes = inlineThumbnailBytes;
|
||||
}
|
||||
if (thumbnailInline && !_thumbnailInline) {
|
||||
_thumbnailInline = thumbnailInline;
|
||||
}
|
||||
const auto update = [](ImagePtr &was, ImagePtr now) {
|
||||
if (!was) {
|
||||
was = now;
|
||||
} else if (was->isDelayedStorageImage()) {
|
||||
if (const auto location = now->location(); location.valid()) {
|
||||
was->setDelayedStorageLocation(
|
||||
Data::FileOrigin(),
|
||||
location);
|
||||
const auto update = [&](PhotoSize size, const ImageWithLocation &data) {
|
||||
auto &image = _images[PhotoSizeIndex(size)];
|
||||
if (data.location.valid()
|
||||
&& (!image.location.valid()
|
||||
|| image.location.width() != data.location.width()
|
||||
|| image.location.height() != data.location.height())) {
|
||||
image.location = data.location;
|
||||
image.byteSize = data.bytesCount;
|
||||
if (!data.preloaded.isNull()) {
|
||||
image.loader = nullptr;
|
||||
if (const auto media = activeMediaView()) {
|
||||
media->set(size, data.preloaded);
|
||||
}
|
||||
} else if (image.loader) {
|
||||
const auto origin = base::take(image.loader)->fileOrigin();
|
||||
load(size, origin);
|
||||
}
|
||||
if (!data.bytes.isEmpty()) {
|
||||
if (const auto cacheKey = image.location.file().cacheKey()) {
|
||||
owner().cache().putIfEmpty(
|
||||
cacheKey,
|
||||
Storage::Cache::Database::TaggedValue(
|
||||
base::duplicate(data.bytes),
|
||||
Data::kImageCacheTag));
|
||||
}
|
||||
}
|
||||
}
|
||||
//if (was->isDelayedStorageImage()) { // #TODO optimize
|
||||
// if (const auto location = now->location(); location.valid()) {
|
||||
// was->setDelayedStorageLocation(
|
||||
// Data::FileOrigin(),
|
||||
// location);
|
||||
// }
|
||||
//}
|
||||
};
|
||||
update(_thumbnailSmall, thumbnailSmall);
|
||||
update(_thumbnail, thumbnail);
|
||||
update(_large, large);
|
||||
update(PhotoSize::Small, small);
|
||||
update(PhotoSize::Thumbnail, thumbnail);
|
||||
update(PhotoSize::Large, large);
|
||||
}
|
||||
|
||||
int PhotoData::width() const {
|
||||
return _large->width();
|
||||
return _images[PhotoSizeIndex(PhotoSize::Large)].location.width();
|
||||
}
|
||||
|
||||
int PhotoData::height() const {
|
||||
return _large->height();
|
||||
return _images[PhotoSizeIndex(PhotoSize::Large)].location.height();
|
||||
}
|
||||
|
||||
PhotoClickHandler::PhotoClickHandler(
|
||||
@@ -255,7 +390,7 @@ void PhotoSaveClickHandler::onClickImpl() const {
|
||||
if (!data->date) {
|
||||
return;
|
||||
} else {
|
||||
data->download(context());
|
||||
data->load(context());
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -14,8 +14,26 @@ class Session;
|
||||
} // namespace Main
|
||||
|
||||
namespace Data {
|
||||
|
||||
class Session;
|
||||
class ReplyPreview;
|
||||
class PhotoMedia;
|
||||
|
||||
inline constexpr auto kPhotoSizeCount = 3;
|
||||
|
||||
enum class PhotoSize : uchar {
|
||||
Small,
|
||||
Thumbnail,
|
||||
Large,
|
||||
};
|
||||
|
||||
[[nodiscard]] inline int PhotoSizeIndex(PhotoSize size) {
|
||||
Expects(static_cast<int>(size) >= 0
|
||||
&& static_cast<int>(size) < kPhotoSizeCount);
|
||||
|
||||
return static_cast<int>(size);
|
||||
}
|
||||
|
||||
} // namespace Data
|
||||
|
||||
class PhotoData final {
|
||||
@@ -25,20 +43,17 @@ public:
|
||||
|
||||
[[nodiscard]] Data::Session &owner() const;
|
||||
[[nodiscard]] Main::Session &session() const;
|
||||
[[nodiscard]] bool isNull() const;
|
||||
|
||||
void automaticLoad(
|
||||
Data::FileOrigin origin,
|
||||
const HistoryItem *item);
|
||||
void automaticLoadSettingsChanged();
|
||||
|
||||
void download(Data::FileOrigin origin);
|
||||
[[nodiscard]] bool loaded() const;
|
||||
[[nodiscard]] bool loading() const;
|
||||
[[nodiscard]] bool displayLoading() const;
|
||||
void cancel();
|
||||
[[nodiscard]] float64 progress() const;
|
||||
[[nodiscard]] int32 loadOffset() const;
|
||||
[[nodiscard]] bool uploading() const;
|
||||
[[nodiscard]] bool cancelled() const;
|
||||
|
||||
void setWaitingForAlbum();
|
||||
[[nodiscard]] bool waitingForAlbum() const;
|
||||
@@ -60,27 +75,39 @@ public:
|
||||
// to (this) received from the server "same" photo.
|
||||
void collectLocalData(not_null<PhotoData*> local);
|
||||
|
||||
bool isNull() const;
|
||||
[[nodiscard]] std::shared_ptr<Data::PhotoMedia> createMediaView();
|
||||
[[nodiscard]] auto activeMediaView() const
|
||||
-> std::shared_ptr<Data::PhotoMedia>;
|
||||
|
||||
void loadThumbnail(Data::FileOrigin origin);
|
||||
void loadThumbnailSmall(Data::FileOrigin origin);
|
||||
Image *thumbnailInline() const;
|
||||
not_null<Image*> thumbnailSmall() const;
|
||||
not_null<Image*> thumbnail() const;
|
||||
void updateImages(
|
||||
const QByteArray &inlineThumbnailBytes,
|
||||
const ImageWithLocation &small,
|
||||
const ImageWithLocation &thumbnail,
|
||||
const ImageWithLocation &large);
|
||||
|
||||
void load(Data::FileOrigin origin);
|
||||
not_null<Image*> large() const;
|
||||
[[nodiscard]] QByteArray inlineThumbnailBytes() const {
|
||||
return _inlineThumbnailBytes;
|
||||
}
|
||||
|
||||
void load(
|
||||
Data::FileOrigin origin,
|
||||
LoadFromCloudSetting fromCloud = LoadFromCloudOrLocal,
|
||||
bool autoLoading = false);
|
||||
|
||||
[[nodiscard]] bool loading(Data::PhotoSize size) const;
|
||||
[[nodiscard]] bool failed(Data::PhotoSize size) const;
|
||||
void load(
|
||||
Data::PhotoSize size,
|
||||
Data::FileOrigin origin,
|
||||
LoadFromCloudSetting fromCloud = LoadFromCloudOrLocal,
|
||||
bool autoLoading = false);
|
||||
[[nodiscard]] const ImageLocation &location(Data::PhotoSize size) const;
|
||||
[[nodiscard]] int imageByteSize(Data::PhotoSize size) const;
|
||||
|
||||
// For now they return size of the 'large' image.
|
||||
int width() const;
|
||||
int height() const;
|
||||
|
||||
void updateImages(
|
||||
ImagePtr thumbnailInline,
|
||||
ImagePtr thumbnailSmall,
|
||||
ImagePtr thumbnail,
|
||||
ImagePtr large);
|
||||
|
||||
PhotoId id = 0;
|
||||
TimeId date = 0;
|
||||
bool hasSticker = false;
|
||||
@@ -91,15 +118,30 @@ public:
|
||||
std::unique_ptr<Data::UploadState> uploadingData;
|
||||
|
||||
private:
|
||||
ImagePtr _thumbnailInline;
|
||||
ImagePtr _thumbnailSmall;
|
||||
ImagePtr _thumbnail;
|
||||
ImagePtr _large;
|
||||
enum class ImageFlag : uchar {
|
||||
Cancelled = 0x01,
|
||||
Failed = 0x02,
|
||||
};
|
||||
friend inline constexpr bool is_flag_type(ImageFlag) { return true; };
|
||||
|
||||
struct Image final {
|
||||
ImageLocation location;
|
||||
std::unique_ptr<FileLoader> loader;
|
||||
int byteSize = 0;
|
||||
base::flags<ImageFlag> flags;
|
||||
};
|
||||
|
||||
void finishLoad(Data::PhotoSize size);
|
||||
void destroyLoader(Data::PhotoSize size);
|
||||
|
||||
QByteArray _inlineThumbnailBytes;
|
||||
std::array<Image, Data::kPhotoSizeCount> _images;
|
||||
|
||||
int32 _dc = 0;
|
||||
uint64 _access = 0;
|
||||
QByteArray _fileReference;
|
||||
std::unique_ptr<Data::ReplyPreview> _replyPreview;
|
||||
std::weak_ptr<Data::PhotoMedia> _media;
|
||||
|
||||
not_null<Data::Session*> _owner;
|
||||
|
||||
|
119
Telegram/SourceFiles/data/data_photo_media.cpp
Normal file
119
Telegram/SourceFiles/data/data_photo_media.cpp
Normal file
@@ -0,0 +1,119 @@
|
||||
/*
|
||||
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_photo_media.h"
|
||||
|
||||
#include "data/data_photo.h"
|
||||
#include "data/data_session.h"
|
||||
#include "data/data_file_origin.h"
|
||||
#include "data/data_auto_download.h"
|
||||
#include "main/main_session.h"
|
||||
#include "history/history_item.h"
|
||||
#include "history/history.h"
|
||||
#include "storage/file_download.h"
|
||||
#include "ui/image/image.h"
|
||||
//#include "facades.h"
|
||||
//#include "app.h"
|
||||
|
||||
namespace Data {
|
||||
|
||||
PhotoMedia::PhotoMedia(not_null<PhotoData*> owner)
|
||||
: _owner(owner) {
|
||||
}
|
||||
|
||||
// NB! Right now DocumentMedia can outlive Main::Session!
|
||||
// In DocumentData::collectLocalData a shared_ptr is sent on_main.
|
||||
// In case this is a problem the ~Gif code should be rewritten.
|
||||
PhotoMedia::~PhotoMedia() = default;
|
||||
|
||||
not_null<PhotoData*> PhotoMedia::owner() const {
|
||||
return _owner;
|
||||
}
|
||||
|
||||
Image *PhotoMedia::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();
|
||||
}
|
||||
|
||||
Image *PhotoMedia::image(PhotoSize size) const {
|
||||
return _images[PhotoSizeIndex(size)].get();
|
||||
}
|
||||
|
||||
void PhotoMedia::wanted(PhotoSize size, Data::FileOrigin origin) {
|
||||
const auto index = PhotoSizeIndex(size);
|
||||
if (!_images[index]) {
|
||||
_owner->load(size, origin);
|
||||
}
|
||||
}
|
||||
|
||||
QSize PhotoMedia::size(PhotoSize size) const {
|
||||
const auto index = PhotoSizeIndex(size);
|
||||
if (const auto image = _images[index].get()) {
|
||||
return image->size();
|
||||
}
|
||||
const auto &location = _owner->location(size);
|
||||
return { location.width(), location.height() };
|
||||
}
|
||||
|
||||
void PhotoMedia::set(PhotoSize size, QImage image) {
|
||||
_images[PhotoSizeIndex(size)] = std::make_unique<Image>(
|
||||
std::make_unique<Images::ImageSource>(std::move(image), "PNG"));
|
||||
_owner->session().downloaderTaskFinished().notify();
|
||||
}
|
||||
|
||||
bool PhotoMedia::loaded() const {
|
||||
const auto index = PhotoSizeIndex(PhotoSize::Large);
|
||||
return (_images[index] != nullptr);
|
||||
}
|
||||
|
||||
float64 PhotoMedia::progress() const {
|
||||
return (_owner->uploading() || _owner->loading())
|
||||
? _owner->progress()
|
||||
: (loaded() ? 1. : 0.);
|
||||
}
|
||||
|
||||
void PhotoMedia::automaticLoad(
|
||||
Data::FileOrigin origin,
|
||||
const HistoryItem *item) {
|
||||
const auto index = PhotoSizeIndex(PhotoSize::Large);
|
||||
if (!item || loaded() || _owner->cancelled()) {
|
||||
return;
|
||||
}
|
||||
const auto loadFromCloud = Data::AutoDownload::Should(
|
||||
_owner->session().settings().autoDownload(),
|
||||
item->history()->peer,
|
||||
_owner);
|
||||
_owner->load(
|
||||
origin,
|
||||
loadFromCloud ? LoadFromCloudOrLocal : LoadFromLocalOnly,
|
||||
true);
|
||||
}
|
||||
|
||||
void PhotoMedia::collectLocalData(not_null<PhotoMedia*> local) {
|
||||
if (const auto image = local->_inlineThumbnail.get()) {
|
||||
_inlineThumbnail = std::make_unique<Image>(
|
||||
std::make_unique<Images::ImageSource>(image->original(), "PNG"));
|
||||
}
|
||||
for (auto i = 0; i != kPhotoSizeCount; ++i) {
|
||||
if (const auto image = local->_images[i].get()) {
|
||||
_images[i] = std::make_unique<Image>(
|
||||
std::make_unique<Images::ImageSource>(
|
||||
image->original(),
|
||||
"PNG"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Data
|
48
Telegram/SourceFiles/data/data_photo_media.h
Normal file
48
Telegram/SourceFiles/data/data_photo_media.h
Normal file
@@ -0,0 +1,48 @@
|
||||
/*
|
||||
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"
|
||||
#include "data/data_photo.h"
|
||||
|
||||
class FileLoader;
|
||||
|
||||
namespace Data {
|
||||
|
||||
class PhotoMedia final {
|
||||
public:
|
||||
explicit PhotoMedia(not_null<PhotoData*> owner);
|
||||
~PhotoMedia();
|
||||
|
||||
[[nodiscard]] not_null<PhotoData*> owner() const;
|
||||
|
||||
[[nodiscard]] Image *thumbnailInline() const;
|
||||
|
||||
[[nodiscard]] Image *image(PhotoSize size) const;
|
||||
[[nodiscard]] QSize size(PhotoSize size) const;
|
||||
void wanted(PhotoSize size, Data::FileOrigin origin);
|
||||
void set(PhotoSize size, QImage image);
|
||||
|
||||
[[nodiscard]] bool loaded() const;
|
||||
[[nodiscard]] float64 progress() const;
|
||||
|
||||
void automaticLoad(Data::FileOrigin origin, const HistoryItem *item);
|
||||
|
||||
void collectLocalData(not_null<PhotoMedia*> local);
|
||||
|
||||
private:
|
||||
// NB! Right now DocumentMedia can outlive Main::Session!
|
||||
// In DocumentData::collectLocalData a shared_ptr is sent on_main.
|
||||
// In case this is a problem the ~Gif code should be rewritten.
|
||||
const not_null<PhotoData*> _owner;
|
||||
mutable std::unique_ptr<Image> _inlineThumbnail;
|
||||
std::array<std::unique_ptr<Image>, kPhotoSizeCount> _images;
|
||||
|
||||
};
|
||||
|
||||
} // namespace Data
|
@@ -11,6 +11,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "data/data_document.h"
|
||||
#include "data/data_document_media.h"
|
||||
#include "data/data_photo.h"
|
||||
#include "data/data_photo_media.h"
|
||||
#include "ui/image/image.h"
|
||||
#include "ui/image/image_source.h"
|
||||
|
||||
@@ -62,18 +63,18 @@ Image *ReplyPreview::image(Data::FileOrigin origin) {
|
||||
return _image.get();
|
||||
}
|
||||
if (_document) {
|
||||
if (!_documentMedia) {
|
||||
_documentMedia = _document->createMediaView();
|
||||
}
|
||||
const auto thumbnail = _documentMedia->thumbnail();
|
||||
if (!_image || (!_good && thumbnail)) {
|
||||
if (!_image || (!_good && _document->hasThumbnail())) {
|
||||
if (!_documentMedia) {
|
||||
_documentMedia = _document->createMediaView();
|
||||
_documentMedia->thumbnailWanted(origin);
|
||||
}
|
||||
const auto thumbnail = _documentMedia->thumbnail();
|
||||
const auto option = _document->isVideoMessage()
|
||||
? Images::Option::Circled
|
||||
: Images::Option::None;
|
||||
if (thumbnail) {
|
||||
prepare(thumbnail, option);
|
||||
} else {
|
||||
_documentMedia->thumbnailWanted(origin);
|
||||
if (const auto image = _documentMedia->thumbnailInline()) {
|
||||
prepare(image, option | Images::Option::Blurred);
|
||||
}
|
||||
@@ -85,22 +86,33 @@ Image *ReplyPreview::image(Data::FileOrigin origin) {
|
||||
}
|
||||
} 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);
|
||||
}
|
||||
if (!_image || !_good) {
|
||||
if (!_photoMedia) {
|
||||
_photoMedia = _photo->createMediaView();
|
||||
_photoMedia->wanted(PhotoSize::Small, origin);
|
||||
}
|
||||
if (const auto small = _photoMedia->image(PhotoSize::Small)) {
|
||||
prepare(small, Images::Option(0));
|
||||
} else if (const auto large = _photoMedia->image(
|
||||
PhotoSize::Large)) {
|
||||
prepare(large, Images::Option(0));
|
||||
} else if (const auto blurred = _photoMedia->thumbnailInline()) {
|
||||
prepare(blurred, Images::Option::Blurred);
|
||||
}
|
||||
|
||||
//if (small->isDelayedStorageImage() // #TODO optimize
|
||||
// && !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();
|
||||
|
@@ -12,6 +12,7 @@ class PhotoData;
|
||||
|
||||
namespace Data {
|
||||
|
||||
class PhotoMedia;
|
||||
class DocumentMedia;
|
||||
struct FileOrigin;
|
||||
|
||||
@@ -26,11 +27,12 @@ private:
|
||||
void prepare(not_null<Image*> image, Images::Options options);
|
||||
|
||||
std::unique_ptr<Image> _image;
|
||||
PhotoData *_photo = nullptr;
|
||||
DocumentData *_document = nullptr;
|
||||
std::shared_ptr<PhotoMedia> _photoMedia;
|
||||
std::shared_ptr<DocumentMedia> _documentMedia;
|
||||
bool _good = false;
|
||||
bool _checked = false;
|
||||
DocumentData *_document = nullptr;
|
||||
PhotoData *_photo = nullptr;
|
||||
std::shared_ptr<DocumentMedia> _documentMedia;
|
||||
|
||||
};
|
||||
|
||||
|
@@ -75,10 +75,10 @@ using ViewElement = HistoryView::Element;
|
||||
// b: crop 320x320
|
||||
// c: crop 640x640
|
||||
// d: crop 1280x1280
|
||||
const auto InlineLevels = QByteArray::fromRawData("i", 1);
|
||||
const auto SmallLevels = QByteArray::fromRawData("sambcxydwi", 10);
|
||||
const auto ThumbnailLevels = QByteArray::fromRawData("mbcxasydwi", 10);
|
||||
const auto LargeLevels = QByteArray::fromRawData("yxwmsdcbai", 10);
|
||||
const auto InlineLevels = "i"_q;
|
||||
const auto SmallLevels = "sambcxydwi"_q;
|
||||
const auto ThumbnailLevels = "mbcxasydwi"_q;
|
||||
const auto LargeLevels = "yxwmsdcbai"_q;
|
||||
|
||||
void CheckForSwitchInlineButton(not_null<HistoryItem*> item) {
|
||||
if (item->out() || !item->hasSwitchInlineButton()) {
|
||||
@@ -127,23 +127,22 @@ std::vector<UnavailableReason> ExtractUnavailableReasons(
|
||||
}) | ranges::to_vector;
|
||||
}
|
||||
|
||||
QByteArray FindDocumentInlineThumbnail(const MTPDdocument &data) {
|
||||
const auto thumbs = data.vthumbs();
|
||||
if (!thumbs) {
|
||||
return QByteArray();
|
||||
}
|
||||
const auto &list = thumbs->v;
|
||||
[[nodiscard]] QByteArray FindInlineThumbnail(
|
||||
const QVector<MTPPhotoSize> &sizes) {
|
||||
const auto i = ranges::find(
|
||||
list,
|
||||
sizes,
|
||||
mtpc_photoStrippedSize,
|
||||
&MTPPhotoSize::type);
|
||||
if (i == list.end()) {
|
||||
return QByteArray();
|
||||
}
|
||||
return i->c_photoStrippedSize().vbytes().v;
|
||||
return (i != sizes.end())
|
||||
? i->c_photoStrippedSize().vbytes().v
|
||||
: QByteArray();
|
||||
}
|
||||
|
||||
MTPPhotoSize FindDocumentThumbnail(const MTPDdocument &data) {
|
||||
[[nodiscard]] QByteArray FindDocumentInlineThumbnail(const MTPDdocument &data) {
|
||||
return FindInlineThumbnail(data.vthumbs().value_or_empty());
|
||||
}
|
||||
|
||||
[[nodiscard]] MTPPhotoSize FindDocumentThumbnail(const MTPDdocument &data) {
|
||||
const auto area = [](const MTPPhotoSize &size) {
|
||||
static constexpr auto kInvalid = 0;
|
||||
return size.match([](const MTPDphotoSizeEmpty &) {
|
||||
@@ -165,7 +164,7 @@ MTPPhotoSize FindDocumentThumbnail(const MTPDdocument &data) {
|
||||
: MTPPhotoSize(MTP_photoSizeEmpty(MTP_string()));
|
||||
}
|
||||
|
||||
std::optional<MTPVideoSize> FindDocumentVideoThumbnail(
|
||||
[[nodiscard]] std::optional<MTPVideoSize> FindDocumentVideoThumbnail(
|
||||
const MTPDdocument &data) {
|
||||
const auto area = [](const MTPVideoSize &size) {
|
||||
static constexpr auto kInvalid = 0;
|
||||
@@ -184,7 +183,11 @@ std::optional<MTPVideoSize> FindDocumentVideoThumbnail(
|
||||
: std::nullopt;
|
||||
}
|
||||
|
||||
rpl::producer<int> PinnedDialogsCountMaxValue(
|
||||
[[nodiscard]] QByteArray FindPhotoInlineThumbnail(const MTPDphoto &data) {
|
||||
return FindInlineThumbnail(data.vsizes().v);
|
||||
}
|
||||
|
||||
[[nodiscard]] rpl::producer<int> PinnedDialogsCountMaxValue(
|
||||
not_null<Main::Session*> session) {
|
||||
return rpl::single(
|
||||
rpl::empty_value()
|
||||
@@ -1099,6 +1102,15 @@ void Session::notifyPhotoLayoutChanged(not_null<const PhotoData*> photo) {
|
||||
}
|
||||
}
|
||||
|
||||
void Session::requestPhotoViewRepaint(not_null<const PhotoData*> photo) {
|
||||
const auto i = _photoItems.find(photo);
|
||||
if (i != end(_photoItems)) {
|
||||
for (const auto item : i->second) {
|
||||
requestItemRepaint(item);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Session::notifyDocumentLayoutChanged(
|
||||
not_null<const DocumentData*> document) {
|
||||
const auto i = _documentItems.find(document);
|
||||
@@ -1153,6 +1165,20 @@ void Session::documentLoadFail(
|
||||
notifyDocumentLayoutChanged(document);
|
||||
}
|
||||
|
||||
void Session::photoLoadProgress(not_null<PhotoData*> photo) {
|
||||
requestPhotoViewRepaint(photo);
|
||||
}
|
||||
|
||||
void Session::photoLoadDone(not_null<PhotoData*> photo) {
|
||||
notifyPhotoLayoutChanged(photo);
|
||||
}
|
||||
|
||||
void Session::photoLoadFail(
|
||||
not_null<PhotoData*> photo,
|
||||
bool started) {
|
||||
notifyPhotoLayoutChanged(photo);
|
||||
}
|
||||
|
||||
void Session::markMediaRead(not_null<const DocumentData*> document) {
|
||||
const auto i = _documentItems.find(document);
|
||||
if (i != end(_documentItems)) {
|
||||
@@ -2174,11 +2200,10 @@ not_null<PhotoData*> Session::processPhoto(
|
||||
const auto image = [&](const QByteArray &levels) {
|
||||
const auto i = find(levels);
|
||||
return (i == thumbs.end())
|
||||
? ImagePtr()
|
||||
: Images::Create(base::duplicate(i->second), "JPG");
|
||||
? ImageWithLocation()
|
||||
: Images::FromImageInMemory(i->second, "JPG");
|
||||
};
|
||||
const auto thumbnailInline = image(InlineLevels);
|
||||
const auto thumbnailSmall = image(SmallLevels);
|
||||
const auto small = image(SmallLevels);
|
||||
const auto thumbnail = image(ThumbnailLevels);
|
||||
const auto large = image(LargeLevels);
|
||||
return data.match([&](const MTPDphoto &data) {
|
||||
@@ -2189,8 +2214,8 @@ not_null<PhotoData*> Session::processPhoto(
|
||||
data.vdate().v,
|
||||
data.vdc_id().v,
|
||||
data.is_has_stickers(),
|
||||
thumbnailInline,
|
||||
thumbnailSmall,
|
||||
QByteArray(),
|
||||
small,
|
||||
thumbnail,
|
||||
large);
|
||||
}, [&](const MTPDphotoEmpty &data) {
|
||||
@@ -2205,10 +2230,10 @@ not_null<PhotoData*> Session::photo(
|
||||
TimeId date,
|
||||
int32 dc,
|
||||
bool hasSticker,
|
||||
const ImagePtr &thumbnailInline,
|
||||
const ImagePtr &thumbnailSmall,
|
||||
const ImagePtr &thumbnail,
|
||||
const ImagePtr &large) {
|
||||
const QByteArray &inlineThumbnailBytes,
|
||||
const ImageWithLocation &small,
|
||||
const ImageWithLocation &thumbnail,
|
||||
const ImageWithLocation &large) {
|
||||
const auto result = photo(id);
|
||||
photoApplyFields(
|
||||
result,
|
||||
@@ -2217,8 +2242,8 @@ not_null<PhotoData*> Session::photo(
|
||||
date,
|
||||
dc,
|
||||
hasSticker,
|
||||
thumbnailInline,
|
||||
thumbnailSmall,
|
||||
inlineThumbnailBytes,
|
||||
small,
|
||||
thumbnail,
|
||||
large);
|
||||
return result;
|
||||
@@ -2252,26 +2277,28 @@ void Session::photoConvert(
|
||||
|
||||
PhotoData *Session::photoFromWeb(
|
||||
const MTPWebDocument &data,
|
||||
ImagePtr thumbnail,
|
||||
const ImageLocation &thumbnailLocation,
|
||||
bool willBecomeNormal) {
|
||||
const auto large = Images::Create(data);
|
||||
const auto large = Images::FromWebDocument(data);
|
||||
const auto thumbnailInline = ImagePtr();
|
||||
if (large->isNull()) {
|
||||
if (!large.valid()) {
|
||||
return nullptr;
|
||||
}
|
||||
auto thumbnailSmall = large;
|
||||
auto small = large;
|
||||
auto thumbnail = thumbnailLocation;
|
||||
if (willBecomeNormal) {
|
||||
const auto width = large->width();
|
||||
const auto height = large->height();
|
||||
const auto width = large.width();
|
||||
const auto height = large.height();
|
||||
|
||||
auto thumbsize = shrinkToKeepAspect(width, height, 100, 100);
|
||||
thumbnailSmall = Images::Create(thumbsize.width(), thumbsize.height());
|
||||
// #TODO optimize
|
||||
//auto thumbsize = shrinkToKeepAspect(width, height, 100, 100);
|
||||
//small = Images::Create(thumbsize.width(), thumbsize.height());
|
||||
|
||||
if (thumbnail->isNull()) {
|
||||
auto mediumsize = shrinkToKeepAspect(width, height, 320, 320);
|
||||
thumbnail = Images::Create(mediumsize.width(), mediumsize.height());
|
||||
}
|
||||
} else if (thumbnail->isNull()) {
|
||||
//if (!thumbnail.valid()) {
|
||||
// auto mediumsize = shrinkToKeepAspect(width, height, 320, 320);
|
||||
// thumbnail = Images::Create(mediumsize.width(), mediumsize.height());
|
||||
//}
|
||||
} else if (!thumbnail.valid()) {
|
||||
thumbnail = large;
|
||||
}
|
||||
|
||||
@@ -2282,10 +2309,10 @@ PhotoData *Session::photoFromWeb(
|
||||
base::unixtime::now(),
|
||||
0,
|
||||
false,
|
||||
thumbnailInline,
|
||||
thumbnailSmall,
|
||||
thumbnail,
|
||||
large);
|
||||
QByteArray(),
|
||||
ImageWithLocation{ .location = small },
|
||||
ImageWithLocation{ .location = thumbnail },
|
||||
ImageWithLocation{ .location = large });
|
||||
}
|
||||
|
||||
void Session::photoApplyFields(
|
||||
@@ -2319,13 +2346,12 @@ void Session::photoApplyFields(
|
||||
};
|
||||
const auto image = [&](const QByteArray &levels) {
|
||||
const auto i = find(levels);
|
||||
return (i == sizes.end()) ? ImagePtr() : Images::Create(data, *i);
|
||||
return (i == sizes.end())
|
||||
? ImageWithLocation()
|
||||
: Images::FromPhotoSize(_session, data, *i);
|
||||
};
|
||||
const auto thumbnailInline = image(InlineLevels);
|
||||
const auto thumbnailSmall = image(SmallLevels);
|
||||
const auto thumbnail = image(ThumbnailLevels);
|
||||
const auto large = image(LargeLevels);
|
||||
if (thumbnailSmall && thumbnail && large) {
|
||||
if (large.location.valid()) {
|
||||
photoApplyFields(
|
||||
photo,
|
||||
data.vaccess_hash().v,
|
||||
@@ -2333,9 +2359,9 @@ void Session::photoApplyFields(
|
||||
data.vdate().v,
|
||||
data.vdc_id().v,
|
||||
data.is_has_stickers(),
|
||||
thumbnailInline,
|
||||
thumbnailSmall,
|
||||
thumbnail,
|
||||
FindPhotoInlineThumbnail(data),
|
||||
image(SmallLevels),
|
||||
image(ThumbnailLevels),
|
||||
large);
|
||||
}
|
||||
}
|
||||
@@ -2347,10 +2373,10 @@ void Session::photoApplyFields(
|
||||
TimeId date,
|
||||
int32 dc,
|
||||
bool hasSticker,
|
||||
const ImagePtr &thumbnailInline,
|
||||
const ImagePtr &thumbnailSmall,
|
||||
const ImagePtr &thumbnail,
|
||||
const ImagePtr &large) {
|
||||
const QByteArray &inlineThumbnailBytes,
|
||||
const ImageWithLocation &small,
|
||||
const ImageWithLocation &thumbnail,
|
||||
const ImageWithLocation &large) {
|
||||
if (!date) {
|
||||
return;
|
||||
}
|
||||
@@ -2358,8 +2384,8 @@ void Session::photoApplyFields(
|
||||
photo->date = date;
|
||||
photo->hasSticker = hasSticker;
|
||||
photo->updateImages(
|
||||
thumbnailInline,
|
||||
thumbnailSmall,
|
||||
inlineThumbnailBytes,
|
||||
small,
|
||||
thumbnail,
|
||||
large);
|
||||
}
|
||||
|
@@ -427,12 +427,17 @@ public:
|
||||
void documentLoadSettingsChanged();
|
||||
|
||||
void notifyPhotoLayoutChanged(not_null<const PhotoData*> photo);
|
||||
void requestPhotoViewRepaint(not_null<const PhotoData*> photo);
|
||||
void notifyDocumentLayoutChanged(
|
||||
not_null<const DocumentData*> document);
|
||||
void requestDocumentViewRepaint(not_null<const DocumentData*> document);
|
||||
void markMediaRead(not_null<const DocumentData*> document);
|
||||
void requestPollViewRepaint(not_null<const PollData*> poll);
|
||||
|
||||
void photoLoadProgress(not_null<PhotoData*> photo);
|
||||
void photoLoadDone(not_null<PhotoData*> photo);
|
||||
void photoLoadFail(not_null<PhotoData*> photo, bool started);
|
||||
|
||||
void documentLoadProgress(not_null<DocumentData*> document);
|
||||
void documentLoadDone(not_null<DocumentData*> document);
|
||||
void documentLoadFail(not_null<DocumentData*> document, bool started);
|
||||
@@ -473,16 +478,16 @@ public:
|
||||
TimeId date,
|
||||
int32 dc,
|
||||
bool hasSticker,
|
||||
const ImagePtr &thumbnailInline,
|
||||
const ImagePtr &thumbnailSmall,
|
||||
const ImagePtr &thumbnail,
|
||||
const ImagePtr &large);
|
||||
const QByteArray &inlineThumbnailBytes,
|
||||
const ImageWithLocation &small,
|
||||
const ImageWithLocation &thumbnail,
|
||||
const ImageWithLocation &large);
|
||||
void photoConvert(
|
||||
not_null<PhotoData*> original,
|
||||
const MTPPhoto &data);
|
||||
[[nodiscard]] PhotoData *photoFromWeb(
|
||||
const MTPWebDocument &data,
|
||||
ImagePtr thumbnail = ImagePtr(),
|
||||
const ImageLocation &thumbnailLocation = ImageLocation(),
|
||||
bool willBecomeNormal = false);
|
||||
|
||||
[[nodiscard]] not_null<DocumentData*> document(DocumentId id);
|
||||
@@ -736,10 +741,10 @@ private:
|
||||
TimeId date,
|
||||
int32 dc,
|
||||
bool hasSticker,
|
||||
const ImagePtr &thumbnailInline,
|
||||
const ImagePtr &thumbnailSmall,
|
||||
const ImagePtr &thumbnail,
|
||||
const ImagePtr &large);
|
||||
const QByteArray &inlineThumbnailBytes,
|
||||
const ImageWithLocation &small,
|
||||
const ImageWithLocation &thumbnail,
|
||||
const ImageWithLocation &large);
|
||||
|
||||
void documentApplyFields(
|
||||
not_null<DocumentData*> document,
|
||||
|
Reference in New Issue
Block a user