Use single emoji sprite and scale + cache it.

This commit is contained in:
John Preston 2018-10-13 20:35:30 +03:00
parent 59a97ffb99
commit b847c8424a
39 changed files with 549 additions and 179 deletions

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.3 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.2 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.2 MiB

View file

@ -1,8 +0,0 @@
<RCC>
<qresource prefix="/gui">
<file alias="art/emoji.webp">../art/emoji.webp</file>
<file alias="art/emoji_125x.webp">../art/emoji_125x.webp</file>
<file alias="art/emoji_150x.webp">../art/emoji_150x.webp</file>
<file alias="art/emoji_200x.webp">../art/emoji_200x.webp</file>
</qresource>
</RCC>

View file

@ -0,0 +1,5 @@
<RCC>
<qresource prefix="/gui">
<file alias="emoji/emoji_1.webp">../emoji/emoji_1.webp</file>
</qresource>
</RCC>

View file

@ -0,0 +1,5 @@
<RCC>
<qresource prefix="/gui">
<file alias="emoji/emoji_2.webp">../emoji/emoji_2.webp</file>
</qresource>
</RCC>

View file

@ -0,0 +1,5 @@
<RCC>
<qresource prefix="/gui">
<file alias="emoji/emoji_3.webp">../emoji/emoji_3.webp</file>
</qresource>
</RCC>

View file

@ -0,0 +1,5 @@
<RCC>
<qresource prefix="/gui">
<file alias="emoji/emoji_4.webp">../emoji/emoji_4.webp</file>
</qresource>
</RCC>

View file

@ -0,0 +1,5 @@
<RCC>
<qresource prefix="/gui">
<file alias="emoji/emoji_5.webp">../emoji/emoji_5.webp</file>
</qresource>
</RCC>

View file

@ -1,5 +0,0 @@
<RCC>
<qresource prefix="/gui">
<file alias="art/emoji_250x.webp">../art/emoji_250x.webp</file>
</qresource>
</RCC>

View file

@ -43,6 +43,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "chat_helpers/message_field.h" #include "chat_helpers/message_field.h"
#include "chat_helpers/stickers.h" #include "chat_helpers/stickers.h"
#include "ui/text_options.h" #include "ui/text_options.h"
#include "ui/emoji_config.h"
#include "storage/localimageloader.h" #include "storage/localimageloader.h"
#include "storage/file_download.h" #include "storage/file_download.h"
#include "storage/file_upload.h" #include "storage/file_upload.h"

View file

@ -77,7 +77,6 @@ namespace {
*pressedLinkItem = nullptr, *pressedLinkItem = nullptr,
*mousedItem = nullptr; *mousedItem = nullptr;
QPixmap *emoji = nullptr, *emojiLarge = nullptr;
style::font monofont; style::font monofont;
struct CornersPixmaps { struct CornersPixmaps {
@ -88,10 +87,6 @@ namespace {
CornersMap cornersMap; CornersMap cornersMap;
QImage cornersMaskLarge[4], cornersMaskSmall[4]; QImage cornersMaskLarge[4], cornersMaskSmall[4];
using EmojiImagesMap = QMap<int, QPixmap>;
EmojiImagesMap MainEmojiMap;
QMap<int, EmojiImagesMap> OtherEmojiMap;
int32 serviceImageCacheSize = 0; int32 serviceImageCacheSize = 0;
} // namespace } // namespace
@ -1440,15 +1435,6 @@ namespace App {
if (family.isEmpty()) family = QFontDatabase::systemFont(QFontDatabase::FixedFont).family(); if (family.isEmpty()) family = QFontDatabase::systemFont(QFontDatabase::FixedFont).family();
::monofont = style::font(st::normalFont->f.pixelSize(), 0, family); ::monofont = style::font(st::normalFont->f.pixelSize(), 0, family);
} }
Ui::Emoji::Init();
if (!::emoji) {
::emoji = new QPixmap(Ui::Emoji::Filename(Ui::Emoji::Index()));
if (cRetina()) ::emoji->setDevicePixelRatio(cRetinaFactor());
}
if (!::emojiLarge) {
::emojiLarge = new QPixmap(Ui::Emoji::Filename(Ui::Emoji::Index() + 1));
if (cRetina()) ::emojiLarge->setDevicePixelRatio(cRetinaFactor());
}
createCorners(); createCorners();
@ -1491,16 +1477,8 @@ namespace App {
} }
void deinitMedia() { void deinitMedia() {
delete ::emoji;
::emoji = nullptr;
delete ::emojiLarge;
::emojiLarge = nullptr;
clearCorners(); clearCorners();
MainEmojiMap.clear();
OtherEmojiMap.clear();
Data::clearGlobalStructures(); Data::clearGlobalStructures();
clearAllImages(); clearAllImages();
@ -1558,30 +1536,6 @@ namespace App {
return ::monofont; return ::monofont;
} }
const QPixmap &emoji() {
return *::emoji;
}
const QPixmap &emojiLarge() {
return *::emojiLarge;
}
const QPixmap &emojiSingle(EmojiPtr emoji, int32 fontHeight) {
auto &map = (fontHeight == st::msgFont->height) ? MainEmojiMap : OtherEmojiMap[fontHeight];
auto i = map.constFind(emoji->index());
if (i == map.cend()) {
auto image = QImage(Ui::Emoji::Size() + st::emojiPadding * cIntRetinaFactor() * 2, fontHeight * cIntRetinaFactor(), QImage::Format_ARGB32_Premultiplied);
if (cRetina()) image.setDevicePixelRatio(cRetinaFactor());
image.fill(Qt::transparent);
{
QPainter p(&image);
emojiDraw(p, emoji, st::emojiPadding * cIntRetinaFactor(), (fontHeight * cIntRetinaFactor() - Ui::Emoji::Size()) / 2);
}
i = map.insert(emoji->index(), App::pixmapFromImageInPlace(std::move(image)));
}
return i.value();
}
void checkImageCacheSize() { void checkImageCacheSize() {
int64 nowImageCacheSize = imageCacheSize(); int64 nowImageCacheSize = imageCacheSize();
if (nowImageCacheSize > serviceImageCacheSize + MemoryForImageCache) { if (nowImageCacheSize > serviceImageCacheSize + MemoryForImageCache) {

View file

@ -186,9 +186,6 @@ namespace App {
void clearMousedItems(); void clearMousedItems();
const style::font &monofont(); const style::font &monofont();
const QPixmap &emoji();
const QPixmap &emojiLarge();
const QPixmap &emojiSingle(EmojiPtr emoji, int32 fontHeight);
void clearHistories(); void clearHistories();

View file

@ -21,6 +21,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "styles/style_chat_helpers.h" #include "styles/style_chat_helpers.h"
#include "ui/widgets/buttons.h" #include "ui/widgets/buttons.h"
#include "ui/widgets/scroll_area.h" #include "ui/widgets/scroll_area.h"
#include "ui/emoji_config.h"
#include "auth_session.h" #include "auth_session.h"
#include "messenger.h" #include "messenger.h"

View file

@ -8,6 +8,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "calls/calls_emoji_fingerprint.h" #include "calls/calls_emoji_fingerprint.h"
#include "calls/calls_call.h" #include "calls/calls_call.h"
#include "ui/emoji_config.h"
namespace Calls { namespace Calls {
namespace { namespace {

View file

@ -18,6 +18,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "ui/effects/ripple_animation.h" #include "ui/effects/ripple_animation.h"
#include "ui/wrap/fade_wrap.h" #include "ui/wrap/fade_wrap.h"
#include "ui/empty_userpic.h" #include "ui/empty_userpic.h"
#include "ui/emoji_config.h"
#include "messenger.h" #include "messenger.h"
#include "mainwindow.h" #include "mainwindow.h"
#include "lang/lang_keys.h" #include "lang/lang_keys.h"
@ -725,12 +726,12 @@ void Panel::paintEvent(QPaintEvent *e) {
if (!_fingerprint.empty()) { if (!_fingerprint.empty()) {
App::roundRect(p, _fingerprintArea, st::callFingerprintBg, ImageRoundRadius::Small); App::roundRect(p, _fingerprintArea, st::callFingerprintBg, ImageRoundRadius::Small);
auto realSize = Ui::Emoji::Size(Ui::Emoji::Index() + 1); const auto realSize = Ui::Emoji::GetSizeLarge();
auto size = realSize / cIntRetinaFactor(); const auto size = realSize / cIntRetinaFactor();
auto left = _fingerprintArea.left() + st::callFingerprintPadding.left(); auto left = _fingerprintArea.left() + st::callFingerprintPadding.left();
auto top = _fingerprintArea.top() + st::callFingerprintPadding.top(); const auto top = _fingerprintArea.top() + st::callFingerprintPadding.top();
for (auto emoji : _fingerprint) { for (const auto emoji : _fingerprint) {
p.drawPixmap(QPoint(left, top), App::emojiLarge(), QRect(emoji->x() * realSize, emoji->y() * realSize, realSize, realSize)); Ui::Emoji::Draw(p, emoji, realSize, left, top);
left += st::callFingerprintSkip + size; left += st::callFingerprintSkip + size;
} }
} }
@ -868,7 +869,7 @@ void Panel::fillFingerprint() {
Expects(_call != nullptr); Expects(_call != nullptr);
_fingerprint = ComputeEmojiFingerprint(_call); _fingerprint = ComputeEmojiFingerprint(_call);
auto realSize = Ui::Emoji::Size(Ui::Emoji::Index() + 1); auto realSize = Ui::Emoji::GetSizeLarge();
auto size = realSize / cIntRetinaFactor(); auto size = realSize / cIntRetinaFactor();
auto count = _fingerprint.size(); auto count = _fingerprint.size();
auto rectWidth = count * size + (count - 1) * st::callFingerprintSkip; auto rectWidth = count * size + (count - 1) * st::callFingerprintSkip;

View file

@ -8,12 +8,13 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "chat_helpers/emoji_list_widget.h" #include "chat_helpers/emoji_list_widget.h"
#include "ui/widgets/buttons.h" #include "ui/widgets/buttons.h"
#include "styles/style_chat_helpers.h"
#include "ui/widgets/shadow.h" #include "ui/widgets/shadow.h"
#include "ui/emoji_config.h"
#include "lang/lang_keys.h" #include "lang/lang_keys.h"
#include "emoji_suggestions_data.h" #include "emoji_suggestions_data.h"
#include "emoji_suggestions_helper.h" #include "emoji_suggestions_helper.h"
#include "facades.h" #include "facades.h"
#include "styles/style_chat_helpers.h"
namespace ChatHelpers { namespace ChatHelpers {
@ -319,18 +320,26 @@ void EmojiColorPicker::drawVariant(Painter &p, int variant) {
if (rtl()) tl.setX(width() - tl.x() - _singleSize.width()); if (rtl()) tl.setX(width() - tl.x() - _singleSize.width());
App::roundRect(p, QRect(tl, _singleSize), st::emojiPanHover, StickerHoverCorners); App::roundRect(p, QRect(tl, _singleSize), st::emojiPanHover, StickerHoverCorners);
} }
auto esize = Ui::Emoji::Size(Ui::Emoji::Index() + 1); const auto esize = Ui::Emoji::GetSizeLarge();
p.drawPixmapLeft(w.x() + (_singleSize.width() - (esize / cIntRetinaFactor())) / 2, w.y() + (_singleSize.height() - (esize / cIntRetinaFactor())) / 2, width(), App::emojiLarge(), QRect(_variants[variant]->x() * esize, _variants[variant]->y() * esize, esize, esize)); Ui::Emoji::Draw(
p,
_variants[variant],
esize,
w.x() + (_singleSize.width() - (esize / cIntRetinaFactor())) / 2,
w.y() + (_singleSize.height() - (esize / cIntRetinaFactor())) / 2);
} }
EmojiListWidget::EmojiListWidget(QWidget *parent, not_null<Window::Controller*> controller) : Inner(parent, controller) EmojiListWidget::EmojiListWidget(
QWidget *parent,
not_null<Window::Controller*> controller)
: Inner(parent, controller)
, _picker(this) { , _picker(this) {
setMouseTracking(true); setMouseTracking(true);
setAttribute(Qt::WA_OpaquePaintEvent); setAttribute(Qt::WA_OpaquePaintEvent);
_picker->hide(); _picker->hide();
_esize = Ui::Emoji::Size(Ui::Emoji::Index() + 1); _esize = Ui::Emoji::GetSizeLarge();
for (auto i = 0; i != kEmojiSectionCount; ++i) { for (auto i = 0; i != kEmojiSectionCount; ++i) {
_counts[i] = Ui::Emoji::GetSectionCount(static_cast<Section>(i)); _counts[i] = Ui::Emoji::GetSectionCount(static_cast<Section>(i));
@ -482,10 +491,12 @@ void EmojiListWidget::paintEvent(QPaintEvent *e) {
if (rtl()) tl.setX(width() - tl.x() - _singleSize.width()); if (rtl()) tl.setX(width() - tl.x() - _singleSize.width());
App::roundRect(p, QRect(tl, _singleSize), st::emojiPanHover, StickerHoverCorners); App::roundRect(p, QRect(tl, _singleSize), st::emojiPanHover, StickerHoverCorners);
} }
auto sourceRect = QRect(_emoji[info.section][index]->x() * _esize, _emoji[info.section][index]->y() * _esize, _esize, _esize); Ui::Emoji::Draw(
auto imageLeft = w.x() + (_singleSize.width() - (_esize / cIntRetinaFactor())) / 2; p,
auto imageTop = w.y() + (_singleSize.height() - (_esize / cIntRetinaFactor())) / 2; _emoji[info.section][index],
p.drawPixmapLeft(imageLeft, imageTop, width(), App::emojiLarge(), sourceRect); _esize,
w.x() + (_singleSize.width() - (_esize / cIntRetinaFactor())) / 2,
w.y() + (_singleSize.height() - (_esize / cIntRetinaFactor())) / 2);
} }
} }
} }

View file

@ -10,6 +10,12 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "chat_helpers/tabbed_selector.h" #include "chat_helpers/tabbed_selector.h"
#include "ui/widgets/tooltip.h" #include "ui/widgets/tooltip.h"
namespace Ui {
namespace Emoji {
enum class Section;
} // namespace Emoji
} // namespace Ui
namespace Window { namespace Window {
class Controller; class Controller;
} // namespace Window } // namespace Window

View file

@ -10,9 +10,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "chat_helpers/emoji_suggestions_helper.h" #include "chat_helpers/emoji_suggestions_helper.h"
#include "ui/effects/ripple_animation.h" #include "ui/effects/ripple_animation.h"
#include "ui/widgets/shadow.h" #include "ui/widgets/shadow.h"
#include "ui/widgets/inner_dropdown.h"
#include "ui/emoji_config.h"
#include "platform/platform_specific.h" #include "platform/platform_specific.h"
#include "styles/style_chat_helpers.h" #include "styles/style_chat_helpers.h"
#include "ui/widgets/inner_dropdown.h"
namespace Ui { namespace Ui {
namespace Emoji { namespace Emoji {
@ -176,24 +177,29 @@ void SuggestionsWidget::paintEvent(QPaintEvent *e) {
if (clip.intersects(topskip)) p.fillRect(clip.intersected(topskip), _st->itemBg); if (clip.intersects(topskip)) p.fillRect(clip.intersected(topskip), _st->itemBg);
if (clip.intersects(bottomskip)) p.fillRect(clip.intersected(bottomskip), _st->itemBg); if (clip.intersects(bottomskip)) p.fillRect(clip.intersected(bottomskip), _st->itemBg);
auto top = _st->skip; const auto top = _st->skip;
p.setFont(_st->itemFont); p.setFont(_st->itemFont);
auto from = floorclamp(clip.top() - top, _rowHeight, 0, _rows.size()); const auto from = floorclamp(clip.top() - top, _rowHeight, 0, _rows.size());
auto to = ceilclamp(clip.top() + clip.height() - top, _rowHeight, 0, _rows.size()); const auto to = ceilclamp(clip.top() + clip.height() - top, _rowHeight, 0, _rows.size());
p.translate(0, top + from * _rowHeight); p.translate(0, top + from * _rowHeight);
for (auto i = from; i != to; ++i) { for (auto i = from; i != to; ++i) {
auto &row = _rows[i]; auto &row = _rows[i];
auto selected = (i == _selected || i == _pressed); const auto selected = (i == _selected || i == _pressed);
p.fillRect(0, 0, width(), _rowHeight, selected ? _st->itemBgOver : _st->itemBg); p.fillRect(0, 0, width(), _rowHeight, selected ? _st->itemBgOver : _st->itemBg);
if (auto ripple = row.ripple()) { if (const auto ripple = row.ripple()) {
ripple->paint(p, 0, 0, width(), ms); ripple->paint(p, 0, 0, width(), ms);
if (ripple->empty()) { if (ripple->empty()) {
row.resetRipple(); row.resetRipple();
} }
} }
auto emoji = row.emoji(); const auto emoji = row.emoji();
auto esize = Ui::Emoji::Size(Ui::Emoji::Index() + 1); const auto esize = Ui::Emoji::GetSizeLarge();
p.drawPixmapLeft((_st->itemPadding.left() - (esize / cIntRetinaFactor())) / 2, (_rowHeight - (esize / cIntRetinaFactor())) / 2, width(), App::emojiLarge(), QRect(emoji->x() * esize, emoji->y() * esize, esize, esize)); Ui::Emoji::Draw(
p,
emoji,
esize,
(_st->itemPadding.left() - (esize / cIntRetinaFactor())) / 2,
(_rowHeight - (esize / cIntRetinaFactor())) / 2);
p.setPen(selected ? _st->itemFgOver : _st->itemFg); p.setPen(selected ? _st->itemFgOver : _st->itemFg);
p.drawTextLeft(_st->itemPadding.left(), _st->itemPadding.top(), width(), row.label()); p.drawTextLeft(_st->itemPadding.left(), _st->itemPadding.top(), width(), row.label());
p.translate(0, _rowHeight); p.translate(0, _rowHeight);

View file

@ -18,6 +18,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "auth_session.h" #include "auth_session.h"
#include "mainwindow.h" #include "mainwindow.h"
#include "ui/toast/toast.h" #include "ui/toast/toast.h"
#include "ui/emoji_config.h"
#include "styles/style_chat_helpers.h" #include "styles/style_chat_helpers.h"
namespace Stickers { namespace Stickers {

View file

@ -321,7 +321,7 @@ bool Generator::writeImages() {
bool Generator::writeSource() { bool Generator::writeSource() {
source_ = std::make_unique<common::CppFile>(outputPath_ + ".cpp", project_); source_ = std::make_unique<common::CppFile>(outputPath_ + ".cpp", project_);
source_->include("emoji_suggestions_data.h").newline(); source_->include("emoji_suggestions_data.h").include("ui/emoji_config.h").newline();
source_->pushNamespace("Ui").pushNamespace("Emoji").pushNamespace(); source_->pushNamespace("Ui").pushNamespace("Emoji").pushNamespace();
source_->stream() << "\ source_->stream() << "\
\n\ \n\
@ -342,6 +342,10 @@ std::vector<One> Items;\n\
source_->popNamespace().newline().pushNamespace("internal"); source_->popNamespace().newline().pushNamespace("internal");
source_->stream() << "\ source_->stream() << "\
\n\ \n\
int FullCount() {\n\
return Items.size();\n\
}\n\
\n\
EmojiPtr ByIndex(int index) {\n\ EmojiPtr ByIndex(int index) {\n\
return (index >= 0 && index < Items.size()) ? &Items[index] : nullptr;\n\ return (index >= 0 && index < Items.size()) ? &Items[index] : nullptr;\n\
}\n\ }\n\
@ -370,7 +374,13 @@ void Init() {\n\
\n\ \n\
Items.reserve(base::array_size(Data));\n\ Items.reserve(base::array_size(Data));\n\
for (auto &data : Data) {\n\ for (auto &data : Data) {\n\
Items.emplace_back(takeString(data.idSize), uint16(data.column), uint16(data.row), bool(data.postfixed), bool(data.variated), data.original ? &Items[data.original - 1] : nullptr, One::CreationTag());\n\ Items.emplace_back(\n\
takeString(data.idSize),\n\
data.original ? &Items[data.original - 1] : nullptr,\n\
uint32(Items.size()),\n\
data.postfixed ? true : false,\n\
data.variated ? true : false,\n\
One::CreationTag());\n\
}\n\ }\n\
InitReplacements();\n\ InitReplacements();\n\
}\n\ }\n\
@ -391,6 +401,7 @@ bool Generator::writeHeader() {
\n\ \n\
void Init();\n\ void Init();\n\
\n\ \n\
int FullCount();\n\
EmojiPtr ByIndex(int index);\n\ EmojiPtr ByIndex(int index);\n\
\n\ \n\
EmojiPtr Find(const QChar *ch, const QChar *end, int *outLength = nullptr);\n\ EmojiPtr Find(const QChar *ch, const QChar *end, int *outLength = nullptr);\n\
@ -414,6 +425,8 @@ EmojiPtr FindReplace(const QChar *ch, const QChar *end, int *outLength = nullptr
\n"; \n";
header->popNamespace().stream() << "\ header->popNamespace().stream() << "\
\n\ \n\
constexpr auto kPostfix = static_cast<ushort>(0xFE0F);\n\
\n\
enum class Section {\n\ enum class Section {\n\
Recent,\n\ Recent,\n\
People,\n\ People,\n\
@ -425,8 +438,6 @@ enum class Section {\n\
Symbols,\n\ Symbols,\n\
};\n\ };\n\
\n\ \n\
int Index();\n\
\n\
int GetSectionCount(Section section);\n\ int GetSectionCount(Section section);\n\
EmojiPack GetSection(Section section);\n\ EmojiPack GetSection(Section section);\n\
\n"; \n";
@ -435,13 +446,11 @@ EmojiPack GetSection(Section section);\n\
template <typename Callback> template <typename Callback>
bool Generator::enumerateWholeList(Callback callback) { bool Generator::enumerateWholeList(Callback callback) {
auto column = 0;
auto row = 0;
auto index = 0; auto index = 0;
auto variated = -1; auto variated = -1;
auto coloredCount = 0; auto coloredCount = 0;
for (auto &item : data_.list) { for (auto &item : data_.list) {
if (!callback(item.id, column, row, item.postfixed, item.variated, item.colored, variated)) { if (!callback(item.id, item.postfixed, item.variated, item.colored, variated)) {
return false; return false;
} }
if (coloredCount > 0 && (item.variated || !item.colored)) { if (coloredCount > 0 && (item.variated || !item.colored)) {
@ -464,10 +473,6 @@ bool Generator::enumerateWholeList(Callback callback) {
} else if (variated >= 0) { } else if (variated >= 0) {
variated = -1; variated = -1;
} }
if (++column == kEmojiInRow) {
column = 0;
++row;
}
++index; ++index;
} }
return true; return true;
@ -476,17 +481,15 @@ bool Generator::enumerateWholeList(Callback callback) {
bool Generator::writeInitCode() { bool Generator::writeInitCode() {
source_->stream() << "\ source_->stream() << "\
struct DataStruct {\n\ struct DataStruct {\n\
ushort original : " << kOriginalBits << ";\n\ uint32 original : " << kOriginalBits << ";\n\
uchar idSize : " << kIdSizeBits << ";\n\ uint32 idSize : " << kIdSizeBits << ";\n\
uchar column : " << kColumnBits << ";\n\ uint32 postfixed : 1;\n\
uchar row : " << kRowBits << ";\n\ uint32 variated : 1;\n\
bool postfixed : 1;\n\
bool variated : 1;\n\
};\n\ };\n\
\n\ \n\
const ushort IdData[] = {"; const ushort IdData[] = {";
startBinary(); startBinary();
if (!enumerateWholeList([this](Id id, int column, int row, bool isPostfixed, bool isVariated, bool isColored, int original) { if (!enumerateWholeList([this](Id id, bool isPostfixed, bool isVariated, bool isColored, int original) {
return writeStringBinary(source_.get(), id); return writeStringBinary(source_.get(), id);
})) { })) {
return false; return false;
@ -498,7 +501,7 @@ const ushort IdData[] = {";
source_->stream() << " };\n\ source_->stream() << " };\n\
\n\ \n\
const DataStruct Data[] = {\n"; const DataStruct Data[] = {\n";
if (!enumerateWholeList([this](Id id, int column, int row, bool isPostfixed, bool isVariated, bool isColored, int original) { if (!enumerateWholeList([this](Id id, bool isPostfixed, bool isVariated, bool isColored, int original) {
if (original + 1 >= (1 << kOriginalBits)) { if (original + 1 >= (1 << kOriginalBits)) {
logDataError() << "Too many entries."; logDataError() << "Too many entries.";
return false; return false;
@ -507,12 +510,8 @@ const DataStruct Data[] = {\n";
logDataError() << "Too large id."; logDataError() << "Too large id.";
return false; return false;
} }
if (column >= (1 << kColumnBits) || row >= (1 << kRowBits)) {
logDataError() << "Bad row-column.";
return false;
}
source_->stream() << "\ source_->stream() << "\
{ ushort(" << (isColored ? (original + 1) : 0) << "), uchar(" << id.size() << "), uchar(" << column << "), uchar(" << row << "), " << (isPostfixed ? "true" : "false") << ", " << (isVariated ? "true" : "false") << " },\n"; { uint32(" << (isColored ? (original + 1) : 0) << "), uint32(" << id.size() << "), uint32(" << (isPostfixed ? "1" : "0") << "), uint32(" << (isVariated ? "1" : "0") << ") },\n";
return true; return true;
})) { })) {
return false; return false;

View file

@ -12,6 +12,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "history/history_location_manager.h" #include "history/history_location_manager.h"
#include "history/view/history_view_element.h" #include "history/view/history_view_element.h"
#include "ui/text_options.h" #include "ui/text_options.h"
#include "ui/emoji_config.h"
#include "storage/storage_shared_media.h" #include "storage/storage_shared_media.h"
#include "storage/localstorage.h" #include "storage/localstorage.h"
#include "data/data_session.h" #include "data/data_session.h"

View file

@ -34,6 +34,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "ui/empty_userpic.h" #include "ui/empty_userpic.h"
#include "ui/grouped_layout.h" #include "ui/grouped_layout.h"
#include "ui/text_options.h" #include "ui/text_options.h"
#include "ui/emoji_config.h"
#include "data/data_session.h" #include "data/data_session.h"
#include "data/data_media_types.h" #include "data/data_media_types.h"

View file

@ -14,6 +14,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "core/file_utilities.h" #include "core/file_utilities.h"
#include "ui/toast/toast.h" #include "ui/toast/toast.h"
#include "ui/special_buttons.h" #include "ui/special_buttons.h"
#include "ui/emoji_config.h"
#include "ui/widgets/buttons.h" #include "ui/widgets/buttons.h"
#include "ui/widgets/inner_dropdown.h" #include "ui/widgets/inner_dropdown.h"
#include "ui/widgets/dropdown_menu.h" #include "ui/widgets/dropdown_menu.h"

View file

@ -29,6 +29,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "ui/focus_persister.h" #include "ui/focus_persister.h"
#include "ui/resize_area.h" #include "ui/resize_area.h"
#include "ui/text_options.h" #include "ui/text_options.h"
#include "ui/emoji_config.h"
#include "ui/toast/toast.h" #include "ui/toast/toast.h"
#include "chat_helpers/message_field.h" #include "chat_helpers/message_field.h"
#include "chat_helpers/stickers.h" #include "chat_helpers/stickers.h"

View file

@ -43,6 +43,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "history/history_location_manager.h" #include "history/history_location_manager.h"
#include "ui/widgets/tooltip.h" #include "ui/widgets/tooltip.h"
#include "ui/text_options.h" #include "ui/text_options.h"
#include "ui/emoji_config.h"
#include "storage/serialize_common.h" #include "storage/serialize_common.h"
#include "window/window_controller.h" #include "window/window_controller.h"
#include "base/qthelp_regex.h" #include "base/qthelp_regex.h"
@ -118,6 +119,7 @@ Messenger::Messenger(not_null<Core::Launcher*> launcher)
style::startManager(); style::startManager();
anim::startManager(); anim::startManager();
Ui::InitTextOptions(); Ui::InitTextOptions();
Ui::Emoji::Init();
Media::Player::start(); Media::Player::start();
DEBUG_LOG(("Application Info: inited...")); DEBUG_LOG(("Application Info: inited..."));
@ -1025,6 +1027,8 @@ Messenger::~Messenger() {
Shortcuts::finish(); Shortcuts::finish();
Ui::Emoji::Clear();
anim::stopManager(); anim::stopManager();
stopWebLoadManager(); stopWebLoadManager();

View file

@ -16,6 +16,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "boxes/send_files_box.h" #include "boxes/send_files_box.h"
#include "window/themes/window_theme.h" #include "window/themes/window_theme.h"
#include "ui/widgets/input_fields.h" #include "ui/widgets/input_fields.h"
#include "ui/emoji_config.h"
#include "export/export_settings.h" #include "export/export_settings.h"
#include "core/crash_reports.h" #include "core/crash_reports.h"
#include "core/update_checker.h" #include "core/update_checker.h"

View file

@ -11,6 +11,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "emoji_config.h" #include "emoji_config.h"
#include "chat_helpers/emoji_suggestions_helper.h" #include "chat_helpers/emoji_suggestions_helper.h"
#include "base/bytes.h"
#include "base/openssl_help.h"
#include "auth_session.h" #include "auth_session.h"
namespace Ui { namespace Ui {
@ -18,8 +20,210 @@ namespace Emoji {
namespace { namespace {
constexpr auto kSaveRecentEmojiTimeout = 3000; constexpr auto kSaveRecentEmojiTimeout = 3000;
constexpr auto kUniversalSize = 72;
constexpr auto kImagesPerRow = 32;
constexpr auto kImageRowsPerSprite = 16;
auto WorkingIndex = -1; constexpr auto kVersion = 1;
class UniversalImages {
public:
void ensureLoaded();
void clear();
void draw(QPainter &p, EmojiPtr emoji, int size, int x, int y) const;
QImage generate(int size, int index) const;
private:
std::vector<QImage> _sprites;
};
auto Scale = -1.;
auto SizeNormal = -1;
auto SizeLarge = -1;
auto SpritesCount = -1;
std::unique_ptr<Instance> InstanceNormal;
std::unique_ptr<Instance> InstanceLarge;
UniversalImages Universal;
std::map<int, QPixmap> MainEmojiMap;
std::map<int, std::map<int, QPixmap>> OtherEmojiMap;
int RowsCount(int index) {
if (index + 1 < SpritesCount) {
return kImageRowsPerSprite;
}
const auto count = internal::FullCount()
- (index * kImagesPerRow * kImageRowsPerSprite);
return (count / kImagesPerRow)
+ ((count % kImagesPerRow) ? 1 : 0);
}
QString CacheFileFolder() {
return cWorkingDir() + "tdata/emoji";
}
QString CacheFilePath(int size, int index) {
return CacheFileFolder()
+ "/cache_"
+ QString::number(size)
+ '_'
+ QString::number(index);
}
void SaveToFile(const QImage &image, int size, int index) {
Expects(image.bytesPerLine() == image.width() * 4);
QFile f(CacheFilePath(size, index));
if (!f.open(QIODevice::WriteOnly)) {
if (!QDir::current().mkpath(CacheFileFolder())
|| !f.open(QIODevice::WriteOnly)) {
LOG(("App Error: Could not open emoji cache '%1' for size %2_%3"
).arg(f.fileName()
).arg(size
).arg(index));
return;
}
}
const auto write = [&](bytes::const_span data) {
return f.write(
reinterpret_cast<const char*>(data.data()),
data.size()
) == data.size();
};
const uint32 header[] = {
uint32(kVersion),
uint32(size),
uint32(image.width()),
uint32(image.height()),
};
const auto data = bytes::const_span(
reinterpret_cast<const bytes::type*>(image.bits()),
image.width() * image.height() * 4);
if (!write(bytes::make_span(header))
|| !write(data)
|| !write(openssl::Sha256(bytes::make_span(header), data))
|| false) {
LOG(("App Error: Could not write emoji cache '%1' for size %2"
).arg(f.fileName()
).arg(size));
}
}
QImage LoadFromFile(int size, int index) {
const auto rows = RowsCount(index);
const auto width = kImagesPerRow * size;
const auto height = rows * size;
const auto fileSize = 4 * sizeof(uint32)
+ (width * height * 4)
+ openssl::kSha256Size;
QFile f(CacheFilePath(size, index));
if (!f.exists()
|| f.size() != fileSize
|| !f.open(QIODevice::ReadOnly)) {
return QImage();
}
const auto read = [&](bytes::span data) {
return f.read(
reinterpret_cast<char*>(data.data()),
data.size()
) == data.size();
};
uint32 header[4] = { 0 };
if (!read(bytes::make_span(header))
|| header[0] != kVersion
|| header[1] != size
|| header[2] != width
|| header[3] != height) {
return QImage();
}
auto result = QImage(
width,
height,
QImage::Format_ARGB32_Premultiplied);
Assert(result.bytesPerLine() == width * 4);
auto data = bytes::make_span(
reinterpret_cast<bytes::type*>(result.bits()),
width * height * 4);
bytes::type signature[openssl::kSha256Size] = { bytes::type() };
if (!read(data)
|| !read(signature)
|| (bytes::compare(
signature,
openssl::Sha256(bytes::make_span(header), data)) != 0)
|| false) {
return QImage();
}
return result;
}
void UniversalImages::ensureLoaded() {
Expects(SpritesCount > 0);
if (!_sprites.empty()) {
return;
}
_sprites.reserve(SpritesCount);
const auto base = qsl(":/gui/emoji/emoji_");
for (auto i = 0; i != SpritesCount; ++i) {
auto image = QImage();
image.load(base + QString::number(i + 1) + ".webp", "WEBP");
_sprites.push_back(std::move(image));
}
}
void UniversalImages::clear() {
_sprites.clear();
}
void UniversalImages::draw(
QPainter &p,
EmojiPtr emoji,
int size,
int x,
int y) const {
Expects(emoji->sprite() < _sprites.size());
const auto factored = (size / p.device()->devicePixelRatio());
const auto large = kUniversalSize;
PainterHighQualityEnabler hq(p);
p.drawImage(
QRect(x, y, factored, factored),
_sprites[emoji->sprite()],
QRect(emoji->column() * large, emoji->row() * large, large, large));
}
QImage UniversalImages::generate(int size, int index) const {
Expects(size > 0);
Expects(index < _sprites.size());
const auto rows = RowsCount(index);
const auto large = kUniversalSize;
const auto &original = _sprites[index];
auto result = QImage(
size * kImagesPerRow,
size * rows,
QImage::Format_ARGB32_Premultiplied);
result.fill(Qt::transparent);
{
QPainter p(&result);
PainterHighQualityEnabler hq(p);
for (auto y = 0; y != rows; ++y) {
for (auto x = 0; x != kImagesPerRow; ++x) {
p.drawImage(
QRect(x * size, y * size, size, size),
original,
QRect(x * large, y * large, large, large));
}
}
}
SaveToFile(result, size, index);
return result;
}
void AppendPartToResult(TextWithEntities &result, const QChar *start, const QChar *from, const QChar *to) { void AppendPartToResult(TextWithEntities &result, const QChar *start, const QChar *from, const QChar *to) {
if (to <= from) { if (to <= from) {
@ -62,23 +266,65 @@ EmojiPtr FindReplacement(const QChar *start, const QChar *end, int *outLength) {
return internal::FindReplace(start, end, outLength); return internal::FindReplace(start, end, outLength);
} }
void ClearUniversalChecked() {
Expects(InstanceNormal != nullptr && InstanceLarge != nullptr);
if (InstanceNormal->cached() && InstanceLarge->cached()) {
Universal.clear();
}
}
} // namespace } // namespace
void Init() { void Init() {
auto scaleForEmoji = cRetina() ? dbisTwo : cScale();
switch (scaleForEmoji) {
case dbisOne: WorkingIndex = 0; break;
case dbisOneAndQuarter: WorkingIndex = 1; break;
case dbisOneAndHalf: WorkingIndex = 2; break;
case dbisTwo: WorkingIndex = 3; break;
};
internal::Init(); internal::Init();
Scale = [] {
if (cRetina()) {
return 2.;
}
switch (cScale()) {
case dbisOne: return 1.;
case dbisOneAndQuarter: return 1.25;
case dbisOneAndHalf: return 1.5;
case dbisTwo: return 2.;
}
Unexpected("cScale() in Ui::Emoji::Init.");
}();
SizeNormal = int(std::round(Scale * 18));
SizeLarge = int(std::round(Scale * 18 * 4 / 3.));
const auto count = internal::FullCount();
const auto persprite = kImagesPerRow * kImageRowsPerSprite;
SpritesCount = (count / persprite) + ((count % persprite) ? 1 : 0);
InstanceNormal = std::make_unique<Instance>(SizeNormal);
InstanceLarge = std::make_unique<Instance>(SizeLarge);
} }
int Index() { void Clear() {
return WorkingIndex; MainEmojiMap.clear();
OtherEmojiMap.clear();
InstanceNormal = nullptr;
InstanceLarge = nullptr;
}
int GetSizeNormal() {
Expects(SizeNormal > 0);
return SizeNormal;
}
int GetSizeLarge() {
Expects(SizeLarge > 0);
return SizeLarge;
}
float64 GetScale() {
Expects(Scale > 0.);
return Scale;
} }
int One::variantsCount() const { int One::variantsCount() const {
@ -93,10 +339,6 @@ EmojiPtr One::variant(int index) const {
return (index >= 0 && index <= variantsCount()) ? (original() + index) : this; return (index >= 0 && index <= variantsCount()) ? (original() + index) : this;
} }
int One::index() const {
return (this - internal::ByIndex(0));
}
QString IdFromOldKey(uint64 oldKey) { QString IdFromOldKey(uint64 oldKey) {
auto code = uint32(oldKey >> 32); auto code = uint32(oldKey >> 32);
auto code2 = uint32(oldKey & 0xFFFFFFFFLLU); auto code2 = uint32(oldKey & 0xFFFFFFFFLLU);
@ -307,5 +549,110 @@ void AddRecent(EmojiPtr emoji) {
} }
} }
const QPixmap &SinglePixmap(EmojiPtr emoji, int fontHeight) {
auto &map = (fontHeight == st::msgFont->height)
? MainEmojiMap
: OtherEmojiMap[fontHeight];
auto i = map.find(emoji->index());
if (i == end(map)) {
auto image = QImage(
SizeNormal + st::emojiPadding * cIntRetinaFactor() * 2,
fontHeight * cIntRetinaFactor(),
QImage::Format_ARGB32_Premultiplied);
if (cRetina()) {
image.setDevicePixelRatio(cRetinaFactor());
}
image.fill(Qt::transparent);
{
QPainter p(&image);
Draw(
p,
emoji,
SizeNormal,
st::emojiPadding * cIntRetinaFactor(),
(fontHeight * cIntRetinaFactor() - SizeNormal) / 2);
}
i = map.emplace(
emoji->index(),
App::pixmapFromImageInPlace(std::move(image))).first;
}
return i->second;
}
void Draw(QPainter &p, EmojiPtr emoji, int size, int x, int y) {
if (size == SizeNormal) {
InstanceNormal->draw(p, emoji, x, y);
} else if (size == SizeLarge) {
InstanceLarge->draw(p, emoji, x, y);
} else {
Unexpected("Size in Ui::Emoji::Draw.");
}
}
Instance::Instance(int size) : _size(size) {
readCache();
if (!cached()) {
Universal.ensureLoaded();
generateCache();
}
}
bool Instance::cached() const {
return (_sprites.size() == SpritesCount);
}
void Instance::draw(QPainter &p, EmojiPtr emoji, int x, int y) {
const auto sprite = emoji->sprite();
if (sprite >= _sprites.size()) {
Universal.draw(p, emoji, _size, x, y);
return;
}
p.drawPixmap(
QPoint(x, y),
_sprites[sprite],
QRect(emoji->column() * _size, emoji->row() * _size, _size, _size));
}
void Instance::readCache() {
for (auto i = 0; i != SpritesCount; ++i) {
auto image = LoadFromFile(_size, i);
if (image.isNull()) {
return;
}
pushSprite(std::move(image));
}
}
void Instance::generateCache() {
const auto size = _size;
const auto index = _sprites.size();
auto [left, right] = base::make_binary_guard();
_generating = std::move(left);
crl::async([=, guard = std::move(right)]() mutable {
crl::on_main([
this,
image = Universal.generate(size, index),
guard = std::move(guard)
]() mutable {
if (!guard.alive()) {
return;
}
pushSprite(std::move(image));
if (cached()) {
ClearUniversalChecked();
} else {
generateCache();
}
});
});
}
void Instance::pushSprite(QImage &&data) {
_sprites.push_back(App::pixmapFromImageInPlace(std::move(data)));
if (cRetina()) {
_sprites.back().setDevicePixelRatio(cRetinaFactor());
}
}
} // namespace Emoji } // namespace Emoji
} // namespace Ui } // namespace Ui

View file

@ -7,16 +7,20 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/ */
#pragma once #pragma once
#include "ui/text/text.h" #include "base/binary_guard.h"
#include "emoji.h" #include "emoji.h"
namespace Ui { namespace Ui {
namespace Emoji { namespace Emoji {
constexpr auto kPostfix = static_cast<ushort>(0xFE0F);
constexpr auto kRecentLimit = 42; constexpr auto kRecentLimit = 42;
void Init(); void Init();
void Clear();
int GetSizeNormal();
int GetSizeLarge();
float64 GetScale();
class One { class One {
struct CreationTag { struct CreationTag {
@ -24,13 +28,12 @@ class One {
public: public:
One(One &&other) = default; One(One &&other) = default;
One(const QString &id, uint16 x, uint16 y, bool hasPostfix, bool colorizable, EmojiPtr original, const CreationTag &) One(const QString &id, EmojiPtr original, uint32 index, bool hasPostfix, bool colorizable, const CreationTag &)
: _id(id) : _id(id)
, _x(x) , _original(original)
, _y(y) , _index(index)
, _hasPostfix(hasPostfix) , _hasPostfix(hasPostfix)
, _colorizable(colorizable) , _colorizable(colorizable) {
, _original(original) {
Expects(!_colorizable || !colored()); Expects(!_colorizable || !colored());
} }
@ -62,25 +65,29 @@ public:
int variantIndex(EmojiPtr variant) const; int variantIndex(EmojiPtr variant) const;
EmojiPtr variant(int index) const; EmojiPtr variant(int index) const;
int index() const; int index() const {
return _index;
}
int sprite() const {
return int(_index >> 9);
}
int row() const {
return int((_index >> 5) & 0x0FU);
}
int column() const {
return int(_index & 0x1FU);
}
QString toUrl() const { QString toUrl() const {
return qsl("emoji://e.") + QString::number(index()); return qsl("emoji://e.") + QString::number(index());
} }
int x() const {
return _x;
}
int y() const {
return _y;
}
private: private:
const QString _id; const QString _id;
const uint16 _x = 0; const EmojiPtr _original = nullptr;
const uint16 _y = 0; const uint32 _index = 0;
const bool _hasPostfix = false; const bool _hasPostfix = false;
const bool _colorizable = false; const bool _colorizable = false;
const EmojiPtr _original = nullptr;
friend void internal::Init(); friend void internal::Init();
@ -123,25 +130,30 @@ inline int ColorIndexFromOldKey(uint64 oldKey) {
return ColorIndexFromCode(uint32(oldKey & 0xFFFFFFFFLLU)); return ColorIndexFromCode(uint32(oldKey & 0xFFFFFFFFLLU));
} }
inline int Size(int index = Index()) {
int sizes[] = { 18, 22, 27, 36, 45 };
return sizes[index];
}
inline QString Filename(int index = Index()) {
const char *EmojiNames[] = {
":/gui/art/emoji.webp",
":/gui/art/emoji_125x.webp",
":/gui/art/emoji_150x.webp",
":/gui/art/emoji_200x.webp",
":/gui/art/emoji_250x.webp",
};
return QString::fromLatin1(EmojiNames[index]);
}
void ReplaceInText(TextWithEntities &result); void ReplaceInText(TextWithEntities &result);
RecentEmojiPack &GetRecent(); RecentEmojiPack &GetRecent();
void AddRecent(EmojiPtr emoji); void AddRecent(EmojiPtr emoji);
const QPixmap &SinglePixmap(EmojiPtr emoji, int fontHeight);
void Draw(QPainter &p, EmojiPtr emoji, int size, int x, int y);
class Instance {
public:
explicit Instance(int size);
bool cached() const;
void draw(QPainter &p, EmojiPtr emoji, int x, int y);
private:
void readCache();
void generateCache();
void pushSprite(QImage &&data);
int _size = 0;
std::vector<QPixmap> _sprites;
base::binary_guard _generating;
};
} // namespace Emoji } // namespace Emoji
} // namespace Ui } // namespace Ui

View file

@ -8,6 +8,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "ui/empty_userpic.h" #include "ui/empty_userpic.h"
#include "data/data_peer.h" #include "data/data_peer.h"
#include "ui/emoji_config.h"
#include "styles/style_history.h" #include "styles/style_history.h"
namespace Ui { namespace Ui {

View file

@ -12,6 +12,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "core/click_handler_types.h" #include "core/click_handler_types.h"
#include "core/crash_reports.h" #include "core/crash_reports.h"
#include "ui/text/text_block.h" #include "ui/text/text_block.h"
#include "ui/emoji_config.h"
#include "lang/lang_keys.h" #include "lang/lang_keys.h"
#include "platform/platform_specific.h" #include "platform/platform_specific.h"
#include "boxes/confirm_box.h" #include "boxes/confirm_box.h"
@ -1441,7 +1442,12 @@ private:
} }
} }
} }
emojiDraw(*_p, static_cast<EmojiBlock*>(currentBlock)->emoji, (glyphX + st::emojiPadding).toInt(), _y + _yDelta + emojiY); Ui::Emoji::Draw(
*_p,
static_cast<EmojiBlock*>(currentBlock)->emoji,
Ui::Emoji::GetSizeNormal(),
(glyphX + st::emojiPadding).toInt(),
_y + _yDelta + emojiY);
// } else if (_p && currentBlock->type() == TextBlockSkip) { // debug // } else if (_p && currentBlock->type() == TextBlockSkip) { // debug
// _p->fillRect(QRect(x.toInt(), _y, currentBlock->width(), static_cast<SkipBlock*>(currentBlock)->height()), QColor(0, 0, 0, 32)); // _p->fillRect(QRect(x.toInt(), _y, currentBlock->width(), static_cast<SkipBlock*>(currentBlock)->height()), QColor(0, 0, 0, 32));
} }
@ -3124,8 +3130,3 @@ void Text::clearFields() {
} }
Text::~Text() = default; Text::~Text() = default;
void emojiDraw(QPainter &p, EmojiPtr e, int x, int y) {
auto size = Ui::Emoji::Size();
p.drawPixmap(QPoint(x, y), App::emoji(), QRect(e->x() * size, e->y() * size, size, size));
}

View file

@ -11,7 +11,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "core/click_handler.h" #include "core/click_handler.h"
#include "ui/text/text_entity.h" #include "ui/text/text_entity.h"
#include "ui/emoji_config.h"
#include "base/flags.h" #include "base/flags.h"
static const QChar TextCommand(0x0010); static const QChar TextCommand(0x0010);
@ -376,5 +375,3 @@ inline bool chIsParagraphSeparator(QChar ch) {
} }
return false; return false;
} }
void emojiDraw(QPainter &p, EmojiPtr e, int x, int y);

View file

@ -10,6 +10,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "auth_session.h" #include "auth_session.h"
#include "lang/lang_tag.h" #include "lang/lang_tag.h"
#include "base/qthelp_url.h" #include "base/qthelp_url.h"
#include "ui/emoji_config.h"
namespace TextUtilities { namespace TextUtilities {
namespace { namespace {

View file

@ -9,6 +9,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "ui/widgets/popup_menu.h" #include "ui/widgets/popup_menu.h"
#include "ui/countryinput.h" #include "ui/countryinput.h"
#include "ui/emoji_config.h"
#include "emoji_suggestions_data.h" #include "emoji_suggestions_data.h"
#include "chat_helpers/emoji_suggestions_helper.h" #include "chat_helpers/emoji_suggestions_helper.h"
#include "window/themes/window_theme.h" #include "window/themes/window_theme.h"
@ -529,7 +530,8 @@ QString AccumulateText(Iterator begin, Iterator end) {
QTextImageFormat PrepareEmojiFormat(EmojiPtr emoji, const QFont &font) { QTextImageFormat PrepareEmojiFormat(EmojiPtr emoji, const QFont &font) {
const auto factor = cIntRetinaFactor(); const auto factor = cIntRetinaFactor();
const auto width = Ui::Emoji::Size() + st::emojiPadding * factor * 2; const auto width = Ui::Emoji::GetSizeNormal()
+ st::emojiPadding * factor * 2;
const auto height = QFontMetrics(font).height() * factor; const auto height = QFontMetrics(font).height() * factor;
auto result = QTextImageFormat(); auto result = QTextImageFormat();
result.setWidth(width / factor); result.setWidth(width / factor);
@ -1236,7 +1238,7 @@ bool InputField::viewportEventInner(QEvent *e) {
QVariant InputField::loadResource(int type, const QUrl &name) { QVariant InputField::loadResource(int type, const QUrl &name) {
const auto imageName = name.toDisplayString(); const auto imageName = name.toDisplayString();
if (const auto emoji = Ui::Emoji::FromUrl(imageName)) { if (const auto emoji = Ui::Emoji::FromUrl(imageName)) {
return QVariant(App::emojiSingle(emoji, _st.font->height)); return QVariant(Ui::Emoji::SinglePixmap(emoji, _st.font->height));
} }
return _inner->QTextEdit::loadResource(type, name); return _inner->QTextEdit::loadResource(type, name);
} }

View file

@ -20,6 +20,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "styles/style_widgets.h" #include "styles/style_widgets.h"
#include "styles/style_chat_helpers.h" #include "styles/style_chat_helpers.h"
#include "ui/widgets/shadow.h" #include "ui/widgets/shadow.h"
#include "ui/emoji_config.h"
#include "window/window_main_menu.h" #include "window/window_main_menu.h"
#include "auth_session.h" #include "auth_session.h"
#include "chat_helpers/stickers.h" #include "chat_helpers/stickers.h"
@ -834,7 +835,7 @@ LayerStackWidget::~LayerStackWidget() {
MediaPreviewWidget::MediaPreviewWidget(QWidget *parent, not_null<Window::Controller*> controller) : TWidget(parent) MediaPreviewWidget::MediaPreviewWidget(QWidget *parent, not_null<Window::Controller*> controller) : TWidget(parent)
, _controller(controller) , _controller(controller)
, _emojiSize(Ui::Emoji::Size(Ui::Emoji::Index() + 1) / cIntRetinaFactor()) { , _emojiSize(Ui::Emoji::GetSizeLarge() / cIntRetinaFactor()) {
setAttribute(Qt::WA_TransparentForMouseEvents); setAttribute(Qt::WA_TransparentForMouseEvents);
subscribe(Auth().downloaderTaskFinished(), [this] { update(); }); subscribe(Auth().downloaderTaskFinished(), [this] { update(); });
} }
@ -860,12 +861,17 @@ void MediaPreviewWidget::paintEvent(QPaintEvent *e) {
p.fillRect(r, st::stickerPreviewBg); p.fillRect(r, st::stickerPreviewBg);
p.drawPixmap((width() - w) / 2, (height() - h) / 2, image); p.drawPixmap((width() - w) / 2, (height() - h) / 2, image);
if (!_emojiList.empty()) { if (!_emojiList.empty()) {
auto emojiCount = _emojiList.size(); const auto emojiCount = _emojiList.size();
auto emojiWidth = (emojiCount * _emojiSize) + (emojiCount - 1) * st::stickerEmojiSkip; const auto emojiWidth = (emojiCount * _emojiSize) + (emojiCount - 1) * st::stickerEmojiSkip;
auto emojiLeft = (width() - emojiWidth) / 2; auto emojiLeft = (width() - emojiWidth) / 2;
auto esize = Ui::Emoji::Size(Ui::Emoji::Index() + 1); const auto esize = Ui::Emoji::GetSizeLarge();
for (auto emoji : _emojiList) { for (const auto emoji : _emojiList) {
p.drawPixmapLeft(emojiLeft, (height() - h) / 2 - (_emojiSize * 2), width(), App::emojiLarge(), QRect(emoji->x() * esize, emoji->y() * esize, esize, esize)); Ui::Emoji::Draw(
p,
emoji,
esize,
emojiLeft,
(height() - h) / 2 - (_emojiSize * 2));
emojiLeft += _emojiSize + st::stickerEmojiSkip; emojiLeft += _emojiSize + st::stickerEmojiSkip;
} }
} }

View file

@ -8,6 +8,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "window/themes/window_theme_preview.h" #include "window/themes/window_theme_preview.h"
#include "window/themes/window_theme.h" #include "window/themes/window_theme.h"
#include "ui/emoji_config.h"
#include "lang/lang_keys.h" #include "lang/lang_keys.h"
#include "platform/platform_window_title.h" #include "platform/platform_window_title.h"
#include "ui/text_options.h" #include "ui/text_options.h"

View file

@ -8,8 +8,11 @@
'variables': { 'variables': {
'qrc_files': [ 'qrc_files': [
'<(res_loc)/qrc/telegram.qrc', '<(res_loc)/qrc/telegram.qrc',
'<(res_loc)/qrc/telegram_emoji.qrc', '<(res_loc)/qrc/telegram_emoji_1.qrc',
'<(res_loc)/qrc/telegram_emoji_large.qrc', '<(res_loc)/qrc/telegram_emoji_2.qrc',
'<(res_loc)/qrc/telegram_emoji_3.qrc',
'<(res_loc)/qrc/telegram_emoji_4.qrc',
'<(res_loc)/qrc/telegram_emoji_5.qrc',
'<(res_loc)/qrc/telegram_sounds.qrc', '<(res_loc)/qrc/telegram_sounds.qrc',
], ],
}, },