Use outlined large emoji.

This commit is contained in:
John Preston 2019-08-05 12:44:40 +01:00
parent 51d350c356
commit e479daca03
4 changed files with 99 additions and 59 deletions

View File

@ -28,7 +28,7 @@ public:
crl::weak_on_queue<EmojiImageLoader> weak, crl::weak_on_queue<EmojiImageLoader> weak,
int id); int id);
[[nodiscard]] QImage prepare(const IsolatedEmoji &emoji); [[nodiscard]] QImage prepare(EmojiPtr emoji);
void switchTo(int id); void switchTo(int id);
private: private:
@ -44,16 +44,11 @@ namespace {
constexpr auto kRefreshTimeout = TimeId(7200); constexpr auto kRefreshTimeout = TimeId(7200);
constexpr auto kUnloadTimeout = 86400 * crl::time(1000); constexpr auto kUnloadTimeout = 86400 * crl::time(1000);
[[nodiscard]] QSize CalculateSize(const IsolatedEmoji &emoji) { [[nodiscard]] QSize SingleSize() {
using namespace rpl::mappers;
const auto single = st::largeEmojiSize; const auto single = st::largeEmojiSize;
const auto skip = st::largeEmojiSkip;
const auto outline = st::largeEmojiOutline; const auto outline = st::largeEmojiOutline;
const auto count = ranges::count_if(emoji.items, _1 != nullptr);
const auto items = single * count + skip * (count - 1);
return QSize( return QSize(
2 * outline + items, 2 * outline + single,
2 * outline + single 2 * outline + single
) * cIntRetinaFactor(); ) * cIntRetinaFactor();
} }
@ -61,7 +56,7 @@ constexpr auto kUnloadTimeout = 86400 * crl::time(1000);
class ImageSource : public Images::Source { class ImageSource : public Images::Source {
public: public:
explicit ImageSource( explicit ImageSource(
const IsolatedEmoji &emoji, EmojiPtr emoji,
not_null<crl::object_on_queue<EmojiImageLoader>*> loader); not_null<crl::object_on_queue<EmojiImageLoader>*> loader);
void load(Data::FileOrigin origin) override; void load(Data::FileOrigin origin) override;
@ -100,7 +95,7 @@ private:
// While HistoryView::Element-s are almost never destroyed // While HistoryView::Element-s are almost never destroyed
// we make loading of the image lazy. // we make loading of the image lazy.
not_null<crl::object_on_queue<EmojiImageLoader>*> _loader; not_null<crl::object_on_queue<EmojiImageLoader>*> _loader;
IsolatedEmoji _emoji; EmojiPtr _emoji = nullptr;
QImage _data; QImage _data;
QByteArray _format; QByteArray _format;
QByteArray _bytes; QByteArray _bytes;
@ -110,11 +105,11 @@ private:
}; };
ImageSource::ImageSource( ImageSource::ImageSource(
const IsolatedEmoji &emoji, EmojiPtr emoji,
not_null<crl::object_on_queue<EmojiImageLoader>*> loader) not_null<crl::object_on_queue<EmojiImageLoader>*> loader)
: _loader(loader) : _loader(loader)
, _emoji(emoji) , _emoji(emoji)
, _size(CalculateSize(emoji)) { , _size(SingleSize()) {
} }
void ImageSource::load(Data::FileOrigin origin) { void ImageSource::load(Data::FileOrigin origin) {
@ -256,31 +251,54 @@ EmojiImageLoader::EmojiImageLoader(
, _unloadTimer(_weak.runner(), [=] { _images->clear(); }) { , _unloadTimer(_weak.runner(), [=] { _images->clear(); }) {
} }
QImage EmojiImageLoader::prepare(const IsolatedEmoji &emoji) { QImage EmojiImageLoader::prepare(EmojiPtr emoji) {
Expects(_images.has_value()); Expects(_images.has_value());
_images->ensureLoaded(); _images->ensureLoaded();
auto result = QImage(
CalculateSize(emoji),
QImage::Format_ARGB32_Premultiplied);
const auto factor = cIntRetinaFactor(); const auto factor = cIntRetinaFactor();
const auto side = st::largeEmojiSize + 2 * st::largeEmojiOutline;
auto tinted = QImage(
QSize(st::largeEmojiSize, st::largeEmojiSize) * factor,
QImage::Format_ARGB32_Premultiplied);
tinted.fill(Qt::white);
{
QPainter p(&tinted);
p.setCompositionMode(QPainter::CompositionMode_DestinationIn);
_images->draw(
p,
emoji,
st::largeEmojiSize * factor,
0,
0);
}
auto result = QImage(
QSize(side, side) * factor,
QImage::Format_ARGB32_Premultiplied);
result.fill(Qt::transparent); result.fill(Qt::transparent);
{ {
QPainter p(&result); QPainter p(&result);
auto x = st::largeEmojiOutline; const auto delta = st::largeEmojiOutline * factor;
const auto y = st::largeEmojiOutline; const auto shifts = std::array<QPoint, 8>{ {
for (const auto &single : emoji.items) { { -1, -1 },
if (!single) { { 0, -1 },
break; { 1, -1 },
{ -1, 0 },
{ 1, 0 },
{ -1, 1 },
{ 0, 1 },
{ 1, 1 },
} };
for (const auto &shift : shifts) {
for (auto i = 0; i != delta; ++i) {
p.drawImage(QPoint(delta, delta) + shift * (i + 1), tinted);
} }
_images->draw(
p,
single,
st::largeEmojiSize * factor,
x * factor,
y * factor);
x += st::largeEmojiSize + st::largeEmojiSkip;
} }
_images->draw(
p,
emoji,
st::largeEmojiSize * factor,
delta,
delta);
} }
_unloadTimer.callOnce(kUnloadTimeout); _unloadTimer.callOnce(kUnloadTimeout);
return result; return result;
@ -356,7 +374,7 @@ DocumentData *EmojiPack::stickerForEmoji(const IsolatedEmoji &emoji) {
return (i != end(_map)) ? i->second.get() : nullptr; return (i != end(_map)) ? i->second.get() : nullptr;
} }
std::shared_ptr<Image> EmojiPack::image(const IsolatedEmoji &emoji) { std::shared_ptr<Image> EmojiPack::image(EmojiPtr emoji) {
const auto i = _images.emplace(emoji, std::weak_ptr<Image>()).first; const auto i = _images.emplace(emoji, std::weak_ptr<Image>()).first;
if (const auto result = i->second.lock()) { if (const auto result = i->second.lock()) {
return result; return result;

View File

@ -40,7 +40,7 @@ public:
void remove(not_null<const HistoryItem*> item); void remove(not_null<const HistoryItem*> item);
[[nodiscard]] DocumentData *stickerForEmoji(const IsolatedEmoji &emoji); [[nodiscard]] DocumentData *stickerForEmoji(const IsolatedEmoji &emoji);
[[nodiscard]] std::shared_ptr<Image> image(const IsolatedEmoji &emoji); [[nodiscard]] std::shared_ptr<Image> image(EmojiPtr emoji);
private: private:
class ImageLoader; class ImageLoader;
@ -62,7 +62,7 @@ private:
base::flat_map< base::flat_map<
IsolatedEmoji, IsolatedEmoji,
base::flat_set<not_null<HistoryItem*>>> _items; base::flat_set<not_null<HistoryItem*>>> _items;
base::flat_map<IsolatedEmoji, std::weak_ptr<Image>> _images; base::flat_map<EmojiPtr, std::weak_ptr<Image>> _images;
mtpRequestId _requestId = 0; mtpRequestId _requestId = 0;
crl::object_on_queue<details::EmojiImageLoader> _imageLoader; crl::object_on_queue<details::EmojiImageLoader> _imageLoader;

View File

@ -20,49 +20,70 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
namespace HistoryView { namespace HistoryView {
namespace { namespace {
std::shared_ptr<Image> ResolveImage( auto ResolveImages(
not_null<Main::Session*> session, not_null<Main::Session*> session,
const Ui::Text::IsolatedEmoji &emoji) { const Ui::Text::IsolatedEmoji &emoji)
return session->emojiStickersPack().image(emoji); -> std::array<std::shared_ptr<Image>, Ui::Text::kIsolatedEmojiLimit> {
const auto single = [&](EmojiPtr emoji) {
return emoji ? session->emojiStickersPack().image(emoji) : nullptr;
};
return { {
single(emoji.items[0]),
single(emoji.items[1]),
single(emoji.items[2]) } };
}
auto NonEmpty(const std::array<std::shared_ptr<Image>, Ui::Text::kIsolatedEmojiLimit> &images) {
using namespace rpl::mappers;
return images | ranges::view::filter(_1 != nullptr);
} }
} // namespace } // namespace
LargeEmoji::LargeEmoji( LargeEmoji::LargeEmoji(
not_null<Element*> parent, not_null<Element*> parent,
Ui::Text::IsolatedEmoji emoji) const Ui::Text::IsolatedEmoji &emoji)
: _parent(parent) : _parent(parent)
, _emoji(emoji) , _images(ResolveImages(&parent->data()->history()->session(), emoji)) {
, _image(ResolveImage(&parent->data()->history()->session(), emoji)) {
} }
QSize LargeEmoji::size() { QSize LargeEmoji::size() {
const auto size = _image->size() / cIntRetinaFactor(); using namespace rpl::mappers;
const auto count = ranges::distance(NonEmpty(_images));
Assert(count > 0);
const auto single = _images[0]->size() / cIntRetinaFactor();
const auto skip = st::largeEmojiSkip - 2 * st::largeEmojiOutline;
const auto inner = count * single.width() + (count - 1) * skip;
const auto &padding = st::largeEmojiPadding; const auto &padding = st::largeEmojiPadding;
_size = QSize( _size = QSize(
padding.left() + size.width() + padding.right(), padding.left() + inner + padding.right(),
padding.top() + size.height() + padding.bottom()); padding.top() + single.height() + padding.bottom());
return _size; return _size;
} }
void LargeEmoji::draw(Painter &p, const QRect &r, bool selected) { void LargeEmoji::draw(Painter &p, const QRect &r, bool selected) {
_image->load(Data::FileOrigin()); auto &&images = NonEmpty(_images);
if (!_image->loaded()) {
return;
}
const auto &padding = st::largeEmojiPadding; const auto &padding = st::largeEmojiPadding;
auto x = r.x() + (r.width() - _size.width()) / 2 + padding.left();
const auto y = r.y() + (r.height() - _size.height()) / 2 + padding.top();
const auto o = Data::FileOrigin(); const auto o = Data::FileOrigin();
const auto w = _size.width() - padding.left() - padding.right(); const auto skip = st::largeEmojiSkip - 2 * st::largeEmojiOutline;
const auto h = _size.height() - padding.top() - padding.bottom(); for (const auto &image : images) {
const auto &c = st::msgStickerOverlay; image->load(Data::FileOrigin());
const auto pixmap = selected const auto w = image->width() / cIntRetinaFactor();
? _image->pixColored(o, c, w, h) if (image->loaded()) {
: _image->pix(o, w, h); const auto h = image->height() / cIntRetinaFactor();
p.drawPixmap( const auto &c = st::msgStickerOverlay;
QPoint( const auto pixmap = selected
r.x() + (r.width() - _size.width()) / 2, ? image->pixColored(o, c, w, h)
r.y() + (r.height() - _size.height()) / 2), : image->pix(o, w, h);
pixmap); p.drawPixmap(x, y, pixmap);
}
x += w + skip;
}
} }
} // namespace HistoryView } // namespace HistoryView

View File

@ -24,15 +24,16 @@ class LargeEmoji final : public UnwrappedMedia::Content {
public: public:
LargeEmoji( LargeEmoji(
not_null<Element*> parent, not_null<Element*> parent,
Ui::Text::IsolatedEmoji emoji); const Ui::Text::IsolatedEmoji &emoji);
QSize size() override; QSize size() override;
void draw(Painter &p, const QRect &r, bool selected) override; void draw(Painter &p, const QRect &r, bool selected) override;
private: private:
const not_null<Element*> _parent; const not_null<Element*> _parent;
const Ui::Text::IsolatedEmoji _emoji; const std::array<
std::shared_ptr<Image> _image; std::shared_ptr<Image>,
Ui::Text::kIsolatedEmojiLimit> _images;
QSize _size; QSize _size;
}; };