mirror of
https://github.com/telegramdesktop/tdesktop
synced 2025-01-11 09:19:35 +00:00
Use outlined large emoji.
This commit is contained in:
parent
51d350c356
commit
e479daca03
@ -28,7 +28,7 @@ public:
|
||||
crl::weak_on_queue<EmojiImageLoader> weak,
|
||||
int id);
|
||||
|
||||
[[nodiscard]] QImage prepare(const IsolatedEmoji &emoji);
|
||||
[[nodiscard]] QImage prepare(EmojiPtr emoji);
|
||||
void switchTo(int id);
|
||||
|
||||
private:
|
||||
@ -44,16 +44,11 @@ namespace {
|
||||
constexpr auto kRefreshTimeout = TimeId(7200);
|
||||
constexpr auto kUnloadTimeout = 86400 * crl::time(1000);
|
||||
|
||||
[[nodiscard]] QSize CalculateSize(const IsolatedEmoji &emoji) {
|
||||
using namespace rpl::mappers;
|
||||
|
||||
[[nodiscard]] QSize SingleSize() {
|
||||
const auto single = st::largeEmojiSize;
|
||||
const auto skip = st::largeEmojiSkip;
|
||||
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(
|
||||
2 * outline + items,
|
||||
2 * outline + single,
|
||||
2 * outline + single
|
||||
) * cIntRetinaFactor();
|
||||
}
|
||||
@ -61,7 +56,7 @@ constexpr auto kUnloadTimeout = 86400 * crl::time(1000);
|
||||
class ImageSource : public Images::Source {
|
||||
public:
|
||||
explicit ImageSource(
|
||||
const IsolatedEmoji &emoji,
|
||||
EmojiPtr emoji,
|
||||
not_null<crl::object_on_queue<EmojiImageLoader>*> loader);
|
||||
|
||||
void load(Data::FileOrigin origin) override;
|
||||
@ -100,7 +95,7 @@ private:
|
||||
// While HistoryView::Element-s are almost never destroyed
|
||||
// we make loading of the image lazy.
|
||||
not_null<crl::object_on_queue<EmojiImageLoader>*> _loader;
|
||||
IsolatedEmoji _emoji;
|
||||
EmojiPtr _emoji = nullptr;
|
||||
QImage _data;
|
||||
QByteArray _format;
|
||||
QByteArray _bytes;
|
||||
@ -110,11 +105,11 @@ private:
|
||||
};
|
||||
|
||||
ImageSource::ImageSource(
|
||||
const IsolatedEmoji &emoji,
|
||||
EmojiPtr emoji,
|
||||
not_null<crl::object_on_queue<EmojiImageLoader>*> loader)
|
||||
: _loader(loader)
|
||||
, _emoji(emoji)
|
||||
, _size(CalculateSize(emoji)) {
|
||||
, _size(SingleSize()) {
|
||||
}
|
||||
|
||||
void ImageSource::load(Data::FileOrigin origin) {
|
||||
@ -256,31 +251,54 @@ EmojiImageLoader::EmojiImageLoader(
|
||||
, _unloadTimer(_weak.runner(), [=] { _images->clear(); }) {
|
||||
}
|
||||
|
||||
QImage EmojiImageLoader::prepare(const IsolatedEmoji &emoji) {
|
||||
QImage EmojiImageLoader::prepare(EmojiPtr emoji) {
|
||||
Expects(_images.has_value());
|
||||
|
||||
_images->ensureLoaded();
|
||||
auto result = QImage(
|
||||
CalculateSize(emoji),
|
||||
QImage::Format_ARGB32_Premultiplied);
|
||||
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);
|
||||
{
|
||||
QPainter p(&result);
|
||||
auto x = st::largeEmojiOutline;
|
||||
const auto y = st::largeEmojiOutline;
|
||||
for (const auto &single : emoji.items) {
|
||||
if (!single) {
|
||||
break;
|
||||
const auto delta = st::largeEmojiOutline * factor;
|
||||
const auto shifts = std::array<QPoint, 8>{ {
|
||||
{ -1, -1 },
|
||||
{ 0, -1 },
|
||||
{ 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);
|
||||
return result;
|
||||
@ -356,7 +374,7 @@ DocumentData *EmojiPack::stickerForEmoji(const IsolatedEmoji &emoji) {
|
||||
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;
|
||||
if (const auto result = i->second.lock()) {
|
||||
return result;
|
||||
|
@ -40,7 +40,7 @@ public:
|
||||
void remove(not_null<const HistoryItem*> item);
|
||||
|
||||
[[nodiscard]] DocumentData *stickerForEmoji(const IsolatedEmoji &emoji);
|
||||
[[nodiscard]] std::shared_ptr<Image> image(const IsolatedEmoji &emoji);
|
||||
[[nodiscard]] std::shared_ptr<Image> image(EmojiPtr emoji);
|
||||
|
||||
private:
|
||||
class ImageLoader;
|
||||
@ -62,7 +62,7 @@ private:
|
||||
base::flat_map<
|
||||
IsolatedEmoji,
|
||||
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;
|
||||
|
||||
crl::object_on_queue<details::EmojiImageLoader> _imageLoader;
|
||||
|
@ -20,49 +20,70 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
namespace HistoryView {
|
||||
namespace {
|
||||
|
||||
std::shared_ptr<Image> ResolveImage(
|
||||
not_null<Main::Session*> session,
|
||||
const Ui::Text::IsolatedEmoji &emoji) {
|
||||
return session->emojiStickersPack().image(emoji);
|
||||
auto ResolveImages(
|
||||
not_null<Main::Session*> session,
|
||||
const Ui::Text::IsolatedEmoji &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
|
||||
|
||||
LargeEmoji::LargeEmoji(
|
||||
not_null<Element*> parent,
|
||||
Ui::Text::IsolatedEmoji emoji)
|
||||
const Ui::Text::IsolatedEmoji &emoji)
|
||||
: _parent(parent)
|
||||
, _emoji(emoji)
|
||||
, _image(ResolveImage(&parent->data()->history()->session(), emoji)) {
|
||||
, _images(ResolveImages(&parent->data()->history()->session(), emoji)) {
|
||||
}
|
||||
|
||||
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;
|
||||
_size = QSize(
|
||||
padding.left() + size.width() + padding.right(),
|
||||
padding.top() + size.height() + padding.bottom());
|
||||
padding.left() + inner + padding.right(),
|
||||
padding.top() + single.height() + padding.bottom());
|
||||
return _size;
|
||||
}
|
||||
|
||||
void LargeEmoji::draw(Painter &p, const QRect &r, bool selected) {
|
||||
_image->load(Data::FileOrigin());
|
||||
if (!_image->loaded()) {
|
||||
return;
|
||||
}
|
||||
auto &&images = NonEmpty(_images);
|
||||
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 w = _size.width() - padding.left() - padding.right();
|
||||
const auto h = _size.height() - padding.top() - padding.bottom();
|
||||
const auto &c = st::msgStickerOverlay;
|
||||
const auto pixmap = selected
|
||||
? _image->pixColored(o, c, w, h)
|
||||
: _image->pix(o, w, h);
|
||||
p.drawPixmap(
|
||||
QPoint(
|
||||
r.x() + (r.width() - _size.width()) / 2,
|
||||
r.y() + (r.height() - _size.height()) / 2),
|
||||
pixmap);
|
||||
const auto skip = st::largeEmojiSkip - 2 * st::largeEmojiOutline;
|
||||
for (const auto &image : images) {
|
||||
image->load(Data::FileOrigin());
|
||||
const auto w = image->width() / cIntRetinaFactor();
|
||||
if (image->loaded()) {
|
||||
const auto h = image->height() / cIntRetinaFactor();
|
||||
const auto &c = st::msgStickerOverlay;
|
||||
const auto pixmap = selected
|
||||
? image->pixColored(o, c, w, h)
|
||||
: image->pix(o, w, h);
|
||||
p.drawPixmap(x, y, pixmap);
|
||||
}
|
||||
x += w + skip;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace HistoryView
|
||||
|
@ -24,15 +24,16 @@ class LargeEmoji final : public UnwrappedMedia::Content {
|
||||
public:
|
||||
LargeEmoji(
|
||||
not_null<Element*> parent,
|
||||
Ui::Text::IsolatedEmoji emoji);
|
||||
const Ui::Text::IsolatedEmoji &emoji);
|
||||
|
||||
QSize size() override;
|
||||
void draw(Painter &p, const QRect &r, bool selected) override;
|
||||
|
||||
private:
|
||||
const not_null<Element*> _parent;
|
||||
const Ui::Text::IsolatedEmoji _emoji;
|
||||
std::shared_ptr<Image> _image;
|
||||
const std::array<
|
||||
std::shared_ptr<Image>,
|
||||
Ui::Text::kIsolatedEmojiLimit> _images;
|
||||
QSize _size;
|
||||
|
||||
};
|
||||
|
Loading…
Reference in New Issue
Block a user