/* 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 "ui/text/text_block.h" #include "base/weak_ptr.h" #include "base/bytes.h" class QColor; class QPainter; namespace Ui { class FrameGenerator; } // namespace Ui namespace Ui::CustomEmoji { class Preview final { public: Preview() = default; Preview(QImage image); Preview(QPainterPath path, float64 scale); void paint(QPainter &p, int x, int y, const QColor &preview); [[nodiscard]] bool image() const; [[nodiscard]] explicit operator bool() const { return !v::is_null(_data); } private: struct ScaledPath { QPainterPath path; float64 scale = 1.; }; void paintPath( QPainter &p, int x, int y, const QColor &preview, const ScaledPath &path); std::variant _data; }; struct PaintFrameResult { bool painted = false; crl::time next = 0; crl::time duration = 0; }; class Cache final { public: Cache(int size); struct Frame { not_null image; QRect source; }; [[nodiscard]] static std::optional FromSerialized( const QByteArray &serialized); [[nodiscard]] QByteArray serialize(); [[nodiscard]] int size() const; [[nodiscard]] int frames() const; [[nodiscard]] Frame frame(int index) const; void reserve(int frames); void add(crl::time duration, const QImage &frame); void finish(); [[nodiscard]] Preview makePreview() const; PaintFrameResult paintCurrentFrame( QPainter &p, int x, int y, crl::time now); [[nodiscard]] int currentFrame() const; private: static constexpr auto kPerRow = 16; [[nodiscard]] int frameRowByteSize() const; [[nodiscard]] int frameByteSize() const; [[nodiscard]] crl::time currentFrameFinishes() const; std::vector _images; std::vector _durations; QImage _full; crl::time _shown = 0; int _frame = 0; int _size = 0; int _frames = 0; bool _finished = false; }; class Loader; class Loading; class Cached final { public: Cached( const QString &entityData, Fn()> unloader, Cache cache); [[nodiscard]] QString entityData() const; PaintFrameResult paint(QPainter &p, int x, int y, crl::time now); [[nodiscard]] Loading unload(); private: Fn()> _unloader; Cache _cache; QString _entityData; }; struct RendererDescriptor { Fn()> generator; Fn put; Fn()> loader; int size = 0; }; class Renderer final : public base::has_weak_ptr { public: explicit Renderer(RendererDescriptor &&descriptor); virtual ~Renderer() = default; PaintFrameResult paint(QPainter &p, int x, int y, crl::time now); [[nodiscard]] std::optional ready(const QString &entityData); [[nodiscard]] std::unique_ptr cancel(); [[nodiscard]] Preview makePreview() const; void setRepaintCallback(Fn repaint); [[nodiscard]] Cache takeCache(); private: void frameReady( std::unique_ptr generator, crl::time duration, QImage frame); void finish(); Cache _cache; std::unique_ptr _generator; Fn _put; Fn _repaint; Fn()> _loader; bool _finished = false; }; struct Caching { std::unique_ptr renderer; QString entityData; Preview preview; }; class Loader { public: using LoadResult = std::variant; [[nodiscard]] virtual QString entityData() = 0; virtual void load(Fn loaded) = 0; [[nodiscard]] virtual bool loading() = 0; virtual void cancel() = 0; [[nodiscard]] virtual Preview preview() = 0; virtual ~Loader() = default; }; class Loading final : public base::has_weak_ptr { public: Loading(std::unique_ptr loader, Preview preview); [[nodiscard]] QString entityData() const; void load(Fn done); [[nodiscard]] bool loading() const; void paint(QPainter &p, int x, int y, const QColor &preview); void cancel(); private: std::unique_ptr _loader; Preview _preview; }; struct RepaintRequest { crl::time when = 0; crl::time duration = 0; }; class Object; class Instance final : public base::has_weak_ptr { public: Instance( Loading loading, Fn, RepaintRequest)> repaintLater); [[nodiscard]] QString entityData() const; void paint( QPainter &p, int x, int y, crl::time now, const QColor &preview, bool paused); void incrementUsage(not_null object); void decrementUsage(not_null object); void repaint(); private: std::variant _state; base::flat_set> _usage; Fn that, RepaintRequest)> _repaintLater; }; class Delegate { public: [[nodiscard]] virtual bool paused() = 0; virtual ~Delegate() = default; }; class Object final : public Ui::Text::CustomEmoji { public: Object(not_null instance, Fn repaint); ~Object(); QString entityData() override; void paint( QPainter &p, int x, int y, crl::time now, const QColor &preview, bool paused) override; void unload() override; void repaint(); private: const not_null _instance; Fn _repaint; bool _using = false; }; } // namespace Ui::CustomEmoji