Use non-exact image previews if available.

This commit is contained in:
John Preston 2022-07-25 12:38:56 +03:00
parent c51837cfdf
commit 31bb08068b
4 changed files with 82 additions and 15 deletions

View file

@ -380,7 +380,7 @@ std::unique_ptr<Ui::Text::CustomEmoji> CustomEmojiManager::create(
documentId,
std::make_unique<Ui::CustomEmoji::Instance>(Loading{
factory(),
Ui::CustomEmoji::Preview()
prepareNonExactPreview(documentId, tag)
}, std::move(repaint))).first;
}
return std::make_unique<Ui::CustomEmoji::Object>(
@ -388,6 +388,27 @@ std::unique_ptr<Ui::Text::CustomEmoji> CustomEmojiManager::create(
std::move(update));
}
Ui::CustomEmoji::Preview CustomEmojiManager::prepareNonExactPreview(
DocumentId documentId,
SizeTag tag) const {
const auto &other = _instances[1 - SizeIndex(tag)];
const auto j = other.find(documentId);
if (j == end(other)) {
return {};
} else if (const auto nonExact = j->second->imagePreview()) {
const auto size = SizeFromTag(tag);
return {
nonExact.image().scaled(
size,
size,
Qt::IgnoreAspectRatio,
Qt::SmoothTransformation),
false,
};
}
return {};
}
std::unique_ptr<Ui::Text::CustomEmoji> CustomEmojiManager::create(
QStringView data,
Fn<void()> update,

View file

@ -78,6 +78,9 @@ private:
void invokeRepaints();
void requestSetFor(not_null<DocumentData*> document);
[[nodiscard]] Ui::CustomEmoji::Preview prepareNonExactPreview(
DocumentId documentId,
SizeTag tag) const;
template <typename LoaderFactory>
[[nodiscard]] std::unique_ptr<Ui::Text::CustomEmoji> create(
DocumentId documentId,

View file

@ -37,22 +37,38 @@ Preview::Preview(QPainterPath path, float64 scale)
: _data(ScaledPath{ std::move(path), scale }) {
}
Preview::Preview(QImage image) : _data(std::move(image)) {
Preview::Preview(QImage image, bool exact)
: _data(Image{ .data = std::move(image), .exact = exact }) {
}
void Preview::paint(QPainter &p, int x, int y, const QColor &preview) {
if (const auto path = std::get_if<ScaledPath>(&_data)) {
paintPath(p, x, y, preview, *path);
} else if (const auto image = std::get_if<QImage>(&_data)) {
} else if (const auto image = std::get_if<Image>(&_data)) {
const auto &data = image->data;
const auto factor = style::DevicePixelRatio();
const auto width = image->width() / factor;
const auto height = image->height() / factor;
p.drawImage(QRect(x, y, width, height), *image);
const auto width = data.width() / factor;
const auto height = data.height() / factor;
p.drawImage(QRect(x, y, width, height), data);
}
}
bool Preview::image() const {
return v::is<QImage>(_data);
bool Preview::isImage() const {
return v::is<Image>(_data);
}
bool Preview::isExactImage() const {
if (const auto image = std::get_if<Image>(&_data)) {
return image->exact;
}
return false;
}
QImage Preview::image() const {
if (const auto image = std::get_if<Image>(&_data)) {
return image->data;
}
return QImage();
}
void Preview::paintPath(
@ -190,7 +206,7 @@ Preview Cache::makePreview() const {
Expects(_frames > 0);
const auto first = frame(0);
return { first.image->copy(first.source) };
return { first.image->copy(first.source), true };
}
void Cache::reserve(int frames) {
@ -349,8 +365,12 @@ PaintFrameResult Cached::paint(QPainter &p, int x, int y, crl::time now) {
return _cache.paintCurrentFrame(p, x, y, now);
}
Preview Cached::makePreview() const {
return _cache.makePreview();
}
Loading Cached::unload() {
return Loading(_unloader(), _cache.makePreview());
return Loading(_unloader(), makePreview());
}
Renderer::Renderer(RendererDescriptor &&descriptor)
@ -516,6 +536,10 @@ void Loading::paint(QPainter &p, int x, int y, const QColor &preview) {
_preview.paint(p, x, y, preview);
}
Preview Loading::imagePreview() const {
return _preview.isImage() ? _preview : Preview();
}
void Loading::cancel() {
_loader->cancel();
invalidate_weak_ptrs(this);
@ -564,7 +588,7 @@ void Instance::paint(
if (!result.painted) {
caching->preview.paint(p, x, y, preview);
} else {
if (!caching->preview.image()) {
if (!caching->preview.isExactImage()) {
caching->preview = caching->renderer->makePreview();
}
if (result.next > now) {
@ -582,6 +606,17 @@ void Instance::paint(
}
}
Preview Instance::imagePreview() const {
if (const auto loading = std::get_if<Loading>(&_state)) {
return loading->imagePreview();
} else if (const auto caching = std::get_if<Caching>(&_state)) {
return caching->preview.isImage() ? caching->preview : Preview();
} else if (const auto cached = std::get_if<Cached>(&_state)) {
return cached->makePreview();
}
return {};
}
void Instance::repaint() {
for (const auto &object : _usage) {
object->repaint();

View file

@ -24,11 +24,13 @@ namespace Ui::CustomEmoji {
class Preview final {
public:
Preview() = default;
Preview(QImage image);
Preview(QImage image, bool exact);
Preview(QPainterPath path, float64 scale);
void paint(QPainter &p, int x, int y, const QColor &preview);
[[nodiscard]] bool image() const;
[[nodiscard]] bool isImage() const;
[[nodiscard]] bool isExactImage() const;
[[nodiscard]] QImage image() const;
[[nodiscard]] explicit operator bool() const {
return !v::is_null(_data);
@ -39,6 +41,10 @@ private:
QPainterPath path;
float64 scale = 1.;
};
struct Image {
QImage data;
bool exact = false;
};
void paintPath(
QPainter &p,
@ -47,7 +53,7 @@ private:
const QColor &preview,
const ScaledPath &path);
std::variant<v::null_t, ScaledPath, QImage> _data;
std::variant<v::null_t, ScaledPath, Image> _data;
};
@ -116,7 +122,7 @@ public:
Cache cache);
[[nodiscard]] QString entityData() const;
[[nodiscard]] Preview makePreview() const;
PaintFrameResult paint(QPainter &p, int x, int y, crl::time now);
[[nodiscard]] Loading unload();
@ -194,6 +200,7 @@ public:
void load(Fn<void(Loader::LoadResult)> done);
[[nodiscard]] bool loading() const;
void paint(QPainter &p, int x, int y, const QColor &preview);
[[nodiscard]] Preview imagePreview() const;
void cancel();
private:
@ -224,6 +231,7 @@ public:
crl::time now,
const QColor &preview,
bool paused);
[[nodiscard]] Preview imagePreview() const;
void incrementUsage(not_null<Object*> object);
void decrementUsage(not_null<Object*> object);