calls_emoji_fingerprint.cpp 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298
  1. /*
  2. This file is part of Telegram Desktop,
  3. the official desktop application for the Telegram messaging service.
  4. For license and copyright information please follow this link:
  5. https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
  6. */
  7. #include "calls/calls_emoji_fingerprint.h"
  8. #include "calls/calls_call.h"
  9. #include "calls/calls_signal_bars.h"
  10. #include "lang/lang_keys.h"
  11. #include "data/data_user.h"
  12. #include "ui/widgets/tooltip.h"
  13. #include "ui/emoji_config.h"
  14. #include "ui/painter.h"
  15. #include "ui/rp_widget.h"
  16. #include "styles/style_calls.h"
  17. namespace Calls {
  18. namespace {
  19. constexpr auto kTooltipShowTimeoutMs = 1000;
  20. const ushort Data[] = {
  21. 0xd83d, 0xde09, 0xd83d, 0xde0d, 0xd83d, 0xde1b, 0xd83d, 0xde2d, 0xd83d, 0xde31, 0xd83d, 0xde21,
  22. 0xd83d, 0xde0e, 0xd83d, 0xde34, 0xd83d, 0xde35, 0xd83d, 0xde08, 0xd83d, 0xde2c, 0xd83d, 0xde07,
  23. 0xd83d, 0xde0f, 0xd83d, 0xdc6e, 0xd83d, 0xdc77, 0xd83d, 0xdc82, 0xd83d, 0xdc76, 0xd83d, 0xdc68,
  24. 0xd83d, 0xdc69, 0xd83d, 0xdc74, 0xd83d, 0xdc75, 0xd83d, 0xde3b, 0xd83d, 0xde3d, 0xd83d, 0xde40,
  25. 0xd83d, 0xdc7a, 0xd83d, 0xde48, 0xd83d, 0xde49, 0xd83d, 0xde4a, 0xd83d, 0xdc80, 0xd83d, 0xdc7d,
  26. 0xd83d, 0xdca9, 0xd83d, 0xdd25, 0xd83d, 0xdca5, 0xd83d, 0xdca4, 0xd83d, 0xdc42, 0xd83d, 0xdc40,
  27. 0xd83d, 0xdc43, 0xd83d, 0xdc45, 0xd83d, 0xdc44, 0xd83d, 0xdc4d, 0xd83d, 0xdc4e, 0xd83d, 0xdc4c,
  28. 0xd83d, 0xdc4a, 0x270c, 0x270b, 0xd83d, 0xdc50, 0xd83d, 0xdc46, 0xd83d, 0xdc47, 0xd83d, 0xdc49,
  29. 0xd83d, 0xdc48, 0xd83d, 0xde4f, 0xd83d, 0xdc4f, 0xd83d, 0xdcaa, 0xd83d, 0xdeb6, 0xd83c, 0xdfc3,
  30. 0xd83d, 0xdc83, 0xd83d, 0xdc6b, 0xd83d, 0xdc6a, 0xd83d, 0xdc6c, 0xd83d, 0xdc6d, 0xd83d, 0xdc85,
  31. 0xd83c, 0xdfa9, 0xd83d, 0xdc51, 0xd83d, 0xdc52, 0xd83d, 0xdc5f, 0xd83d, 0xdc5e, 0xd83d, 0xdc60,
  32. 0xd83d, 0xdc55, 0xd83d, 0xdc57, 0xd83d, 0xdc56, 0xd83d, 0xdc59, 0xd83d, 0xdc5c, 0xd83d, 0xdc53,
  33. 0xd83c, 0xdf80, 0xd83d, 0xdc84, 0xd83d, 0xdc9b, 0xd83d, 0xdc99, 0xd83d, 0xdc9c, 0xd83d, 0xdc9a,
  34. 0xd83d, 0xdc8d, 0xd83d, 0xdc8e, 0xd83d, 0xdc36, 0xd83d, 0xdc3a, 0xd83d, 0xdc31, 0xd83d, 0xdc2d,
  35. 0xd83d, 0xdc39, 0xd83d, 0xdc30, 0xd83d, 0xdc38, 0xd83d, 0xdc2f, 0xd83d, 0xdc28, 0xd83d, 0xdc3b,
  36. 0xd83d, 0xdc37, 0xd83d, 0xdc2e, 0xd83d, 0xdc17, 0xd83d, 0xdc34, 0xd83d, 0xdc11, 0xd83d, 0xdc18,
  37. 0xd83d, 0xdc3c, 0xd83d, 0xdc27, 0xd83d, 0xdc25, 0xd83d, 0xdc14, 0xd83d, 0xdc0d, 0xd83d, 0xdc22,
  38. 0xd83d, 0xdc1b, 0xd83d, 0xdc1d, 0xd83d, 0xdc1c, 0xd83d, 0xdc1e, 0xd83d, 0xdc0c, 0xd83d, 0xdc19,
  39. 0xd83d, 0xdc1a, 0xd83d, 0xdc1f, 0xd83d, 0xdc2c, 0xd83d, 0xdc0b, 0xd83d, 0xdc10, 0xd83d, 0xdc0a,
  40. 0xd83d, 0xdc2b, 0xd83c, 0xdf40, 0xd83c, 0xdf39, 0xd83c, 0xdf3b, 0xd83c, 0xdf41, 0xd83c, 0xdf3e,
  41. 0xd83c, 0xdf44, 0xd83c, 0xdf35, 0xd83c, 0xdf34, 0xd83c, 0xdf33, 0xd83c, 0xdf1e, 0xd83c, 0xdf1a,
  42. 0xd83c, 0xdf19, 0xd83c, 0xdf0e, 0xd83c, 0xdf0b, 0x26a1, 0x2614, 0x2744, 0x26c4, 0xd83c, 0xdf00,
  43. 0xd83c, 0xdf08, 0xd83c, 0xdf0a, 0xd83c, 0xdf93, 0xd83c, 0xdf86, 0xd83c, 0xdf83, 0xd83d, 0xdc7b,
  44. 0xd83c, 0xdf85, 0xd83c, 0xdf84, 0xd83c, 0xdf81, 0xd83c, 0xdf88, 0xd83d, 0xdd2e, 0xd83c, 0xdfa5,
  45. 0xd83d, 0xdcf7, 0xd83d, 0xdcbf, 0xd83d, 0xdcbb, 0x260e, 0xd83d, 0xdce1, 0xd83d, 0xdcfa, 0xd83d,
  46. 0xdcfb, 0xd83d, 0xdd09, 0xd83d, 0xdd14, 0x23f3, 0x23f0, 0x231a, 0xd83d, 0xdd12, 0xd83d, 0xdd11,
  47. 0xd83d, 0xdd0e, 0xd83d, 0xdca1, 0xd83d, 0xdd26, 0xd83d, 0xdd0c, 0xd83d, 0xdd0b, 0xd83d, 0xdebf,
  48. 0xd83d, 0xdebd, 0xd83d, 0xdd27, 0xd83d, 0xdd28, 0xd83d, 0xdeaa, 0xd83d, 0xdeac, 0xd83d, 0xdca3,
  49. 0xd83d, 0xdd2b, 0xd83d, 0xdd2a, 0xd83d, 0xdc8a, 0xd83d, 0xdc89, 0xd83d, 0xdcb0, 0xd83d, 0xdcb5,
  50. 0xd83d, 0xdcb3, 0x2709, 0xd83d, 0xdceb, 0xd83d, 0xdce6, 0xd83d, 0xdcc5, 0xd83d, 0xdcc1, 0x2702,
  51. 0xd83d, 0xdccc, 0xd83d, 0xdcce, 0x2712, 0x270f, 0xd83d, 0xdcd0, 0xd83d, 0xdcda, 0xd83d, 0xdd2c,
  52. 0xd83d, 0xdd2d, 0xd83c, 0xdfa8, 0xd83c, 0xdfac, 0xd83c, 0xdfa4, 0xd83c, 0xdfa7, 0xd83c, 0xdfb5,
  53. 0xd83c, 0xdfb9, 0xd83c, 0xdfbb, 0xd83c, 0xdfba, 0xd83c, 0xdfb8, 0xd83d, 0xdc7e, 0xd83c, 0xdfae,
  54. 0xd83c, 0xdccf, 0xd83c, 0xdfb2, 0xd83c, 0xdfaf, 0xd83c, 0xdfc8, 0xd83c, 0xdfc0, 0x26bd, 0x26be,
  55. 0xd83c, 0xdfbe, 0xd83c, 0xdfb1, 0xd83c, 0xdfc9, 0xd83c, 0xdfb3, 0xd83c, 0xdfc1, 0xd83c, 0xdfc7,
  56. 0xd83c, 0xdfc6, 0xd83c, 0xdfca, 0xd83c, 0xdfc4, 0x2615, 0xd83c, 0xdf7c, 0xd83c, 0xdf7a, 0xd83c,
  57. 0xdf77, 0xd83c, 0xdf74, 0xd83c, 0xdf55, 0xd83c, 0xdf54, 0xd83c, 0xdf5f, 0xd83c, 0xdf57, 0xd83c,
  58. 0xdf71, 0xd83c, 0xdf5a, 0xd83c, 0xdf5c, 0xd83c, 0xdf61, 0xd83c, 0xdf73, 0xd83c, 0xdf5e, 0xd83c,
  59. 0xdf69, 0xd83c, 0xdf66, 0xd83c, 0xdf82, 0xd83c, 0xdf70, 0xd83c, 0xdf6a, 0xd83c, 0xdf6b, 0xd83c,
  60. 0xdf6d, 0xd83c, 0xdf6f, 0xd83c, 0xdf4e, 0xd83c, 0xdf4f, 0xd83c, 0xdf4a, 0xd83c, 0xdf4b, 0xd83c,
  61. 0xdf52, 0xd83c, 0xdf47, 0xd83c, 0xdf49, 0xd83c, 0xdf53, 0xd83c, 0xdf51, 0xd83c, 0xdf4c, 0xd83c,
  62. 0xdf50, 0xd83c, 0xdf4d, 0xd83c, 0xdf46, 0xd83c, 0xdf45, 0xd83c, 0xdf3d, 0xd83c, 0xdfe1, 0xd83c,
  63. 0xdfe5, 0xd83c, 0xdfe6, 0x26ea, 0xd83c, 0xdff0, 0x26fa, 0xd83c, 0xdfed, 0xd83d, 0xddfb, 0xd83d,
  64. 0xddfd, 0xd83c, 0xdfa0, 0xd83c, 0xdfa1, 0x26f2, 0xd83c, 0xdfa2, 0xd83d, 0xdea2, 0xd83d, 0xdea4,
  65. 0x2693, 0xd83d, 0xde80, 0x2708, 0xd83d, 0xde81, 0xd83d, 0xde82, 0xd83d, 0xde8b, 0xd83d, 0xde8e,
  66. 0xd83d, 0xde8c, 0xd83d, 0xde99, 0xd83d, 0xde97, 0xd83d, 0xde95, 0xd83d, 0xde9b, 0xd83d, 0xdea8,
  67. 0xd83d, 0xde94, 0xd83d, 0xde92, 0xd83d, 0xde91, 0xd83d, 0xdeb2, 0xd83d, 0xdea0, 0xd83d, 0xde9c,
  68. 0xd83d, 0xdea6, 0x26a0, 0xd83d, 0xdea7, 0x26fd, 0xd83c, 0xdfb0, 0xd83d, 0xddff, 0xd83c, 0xdfaa,
  69. 0xd83c, 0xdfad, 0xd83c, 0xddef, 0xd83c, 0xddf5, 0xd83c, 0xddf0, 0xd83c, 0xddf7, 0xd83c, 0xdde9,
  70. 0xd83c, 0xddea, 0xd83c, 0xdde8, 0xd83c, 0xddf3, 0xd83c, 0xddfa, 0xd83c, 0xddf8, 0xd83c, 0xddeb,
  71. 0xd83c, 0xddf7, 0xd83c, 0xddea, 0xd83c, 0xddf8, 0xd83c, 0xddee, 0xd83c, 0xddf9, 0xd83c, 0xddf7,
  72. 0xd83c, 0xddfa, 0xd83c, 0xddec, 0xd83c, 0xdde7, 0x0031, 0x20e3, 0x0032, 0x20e3, 0x0033, 0x20e3,
  73. 0x0034, 0x20e3, 0x0035, 0x20e3, 0x0036, 0x20e3, 0x0037, 0x20e3, 0x0038, 0x20e3, 0x0039, 0x20e3,
  74. 0x0030, 0x20e3, 0xd83d, 0xdd1f, 0x2757, 0x2753, 0x2665, 0x2666, 0xd83d, 0xdcaf, 0xd83d, 0xdd17,
  75. 0xd83d, 0xdd31, 0xd83d, 0xdd34, 0xd83d, 0xdd35, 0xd83d, 0xdd36, 0xd83d, 0xdd37 };
  76. const ushort Offsets[] = {
  77. 0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22,
  78. 24, 26, 28, 30, 32, 34, 36, 38, 40, 42, 44, 46,
  79. 48, 50, 52, 54, 56, 58, 60, 62, 64, 66, 68, 70,
  80. 72, 74, 76, 78, 80, 82, 84, 86, 87, 88, 90, 92,
  81. 94, 96, 98, 100, 102, 104, 106, 108, 110, 112, 114, 116,
  82. 118, 120, 122, 124, 126, 128, 130, 132, 134, 136, 138, 140,
  83. 142, 144, 146, 148, 150, 152, 154, 156, 158, 160, 162, 164,
  84. 166, 168, 170, 172, 174, 176, 178, 180, 182, 184, 186, 188,
  85. 190, 192, 194, 196, 198, 200, 202, 204, 206, 208, 210, 212,
  86. 214, 216, 218, 220, 222, 224, 226, 228, 230, 232, 234, 236,
  87. 238, 240, 242, 244, 246, 248, 250, 252, 254, 256, 258, 259,
  88. 260, 261, 262, 264, 266, 268, 270, 272, 274, 276, 278, 280,
  89. 282, 284, 286, 288, 290, 292, 294, 295, 297, 299, 301, 303,
  90. 305, 306, 307, 308, 310, 312, 314, 316, 318, 320, 322, 324,
  91. 326, 328, 330, 332, 334, 336, 338, 340, 342, 344, 346, 348,
  92. 350, 351, 353, 355, 357, 359, 360, 362, 364, 365, 366, 368,
  93. 370, 372, 374, 376, 378, 380, 382, 384, 386, 388, 390, 392,
  94. 394, 396, 398, 400, 402, 404, 406, 407, 408, 410, 412, 414,
  95. 416, 418, 420, 422, 424, 426, 427, 429, 431, 433, 435, 437,
  96. 439, 441, 443, 445, 447, 449, 451, 453, 455, 457, 459, 461,
  97. 463, 465, 467, 469, 471, 473, 475, 477, 479, 481, 483, 485,
  98. 487, 489, 491, 493, 495, 497, 499, 501, 503, 505, 507, 508,
  99. 510, 511, 513, 515, 517, 519, 521, 522, 524, 526, 528, 529,
  100. 531, 532, 534, 536, 538, 540, 542, 544, 546, 548, 550, 552,
  101. 554, 556, 558, 560, 562, 564, 566, 567, 569, 570, 572, 574,
  102. 576, 578, 582, 586, 590, 594, 598, 602, 606, 610, 614, 618,
  103. 620, 622, 624, 626, 628, 630, 632, 634, 636, 638, 640, 641,
  104. 642, 643, 644, 646, 648, 650, 652, 654, 656, 658 };
  105. uint64 ComputeEmojiIndex(bytes::const_span bytes) {
  106. Expects(bytes.size() == 8);
  107. return ((gsl::to_integer<uint64>(bytes[0]) & 0x7F) << 56)
  108. | (gsl::to_integer<uint64>(bytes[1]) << 48)
  109. | (gsl::to_integer<uint64>(bytes[2]) << 40)
  110. | (gsl::to_integer<uint64>(bytes[3]) << 32)
  111. | (gsl::to_integer<uint64>(bytes[4]) << 24)
  112. | (gsl::to_integer<uint64>(bytes[5]) << 16)
  113. | (gsl::to_integer<uint64>(bytes[6]) << 8)
  114. | (gsl::to_integer<uint64>(bytes[7]));
  115. }
  116. } // namespace
  117. std::vector<EmojiPtr> ComputeEmojiFingerprint(not_null<Call*> call) {
  118. auto result = std::vector<EmojiPtr>();
  119. constexpr auto EmojiCount = (base::array_size(Offsets) - 1);
  120. for (auto index = 0; index != EmojiCount; ++index) {
  121. auto offset = Offsets[index];
  122. auto size = Offsets[index + 1] - offset;
  123. auto string = QString::fromRawData(
  124. reinterpret_cast<const QChar*>(Data + offset),
  125. size);
  126. auto emoji = Ui::Emoji::Find(string);
  127. Assert(emoji != nullptr);
  128. }
  129. if (call->isKeyShaForFingerprintReady()) {
  130. auto sha256 = call->getKeyShaForFingerprint();
  131. constexpr auto kPartSize = 8;
  132. for (auto partOffset = 0; partOffset != sha256.size(); partOffset += kPartSize) {
  133. auto value = ComputeEmojiIndex(gsl::make_span(sha256).subspan(partOffset, kPartSize));
  134. auto index = value % EmojiCount;
  135. auto offset = Offsets[index];
  136. auto size = Offsets[index + 1] - offset;
  137. auto string = QString::fromRawData(
  138. reinterpret_cast<const QChar*>(Data + offset),
  139. size);
  140. auto emoji = Ui::Emoji::Find(string);
  141. Assert(emoji != nullptr);
  142. result.push_back(emoji);
  143. }
  144. }
  145. return result;
  146. }
  147. object_ptr<Ui::RpWidget> CreateFingerprintAndSignalBars(
  148. not_null<QWidget*> parent,
  149. not_null<Call*> call) {
  150. class EmojiTooltipShower final : public Ui::AbstractTooltipShower {
  151. public:
  152. EmojiTooltipShower(not_null<QWidget*> window, const QString &text)
  153. : _window(window)
  154. , _text(text) {
  155. }
  156. QString tooltipText() const override {
  157. return _text;
  158. }
  159. QPoint tooltipPos() const override {
  160. return QCursor::pos();
  161. }
  162. bool tooltipWindowActive() const override {
  163. return _window->isActiveWindow();
  164. }
  165. private:
  166. const not_null<QWidget*> _window;
  167. const QString _text;
  168. };
  169. auto result = object_ptr<Ui::RpWidget>(parent);
  170. const auto raw = result.data();
  171. // Emoji tooltip.
  172. const auto shower = raw->lifetime().make_state<EmojiTooltipShower>(
  173. parent->window(),
  174. tr::lng_call_fingerprint_tooltip(
  175. tr::now,
  176. lt_user,
  177. call->user()->name()));
  178. raw->setMouseTracking(true);
  179. raw->events(
  180. ) | rpl::start_with_next([=](not_null<QEvent*> e) {
  181. if (e->type() == QEvent::MouseMove) {
  182. Ui::Tooltip::Show(kTooltipShowTimeoutMs, shower);
  183. } else if (e->type() == QEvent::Leave) {
  184. Ui::Tooltip::Hide();
  185. }
  186. }, raw->lifetime());
  187. // Signal bars.
  188. const auto bars = Ui::CreateChild<SignalBars>(
  189. raw,
  190. call,
  191. st::callPanelSignalBars);
  192. bars->setAttribute(Qt::WA_TransparentForMouseEvents);
  193. // Geometry.
  194. const auto print = ComputeEmojiFingerprint(call);
  195. auto realSize = Ui::Emoji::GetSizeNormal();
  196. auto size = realSize / style::DevicePixelRatio();
  197. auto count = print.size();
  198. const auto printSize = QSize(
  199. count * size + (count - 1) * st::callFingerprintSkip,
  200. size);
  201. const auto fullPrintSize = QRect(
  202. QPoint(),
  203. printSize
  204. ).marginsAdded(st::callFingerprintPadding).size();
  205. const auto fullBarsSize = bars->rect().marginsAdded(
  206. st::callSignalBarsPadding
  207. ).size();
  208. const auto fullSize = QSize(
  209. (fullPrintSize.width()
  210. + st::callFingerprintSignalBarsSkip
  211. + fullBarsSize.width()),
  212. fullPrintSize.height());
  213. raw->resize(fullSize);
  214. bars->moveToRight(
  215. st::callSignalBarsPadding.right(),
  216. st::callSignalBarsPadding.top());
  217. // Paint.
  218. const auto background = raw->lifetime().make_state<QImage>(
  219. fullSize * style::DevicePixelRatio(),
  220. QImage::Format_ARGB32_Premultiplied);
  221. background->setDevicePixelRatio(style::DevicePixelRatio());
  222. rpl::merge(
  223. rpl::single(rpl::empty),
  224. Ui::Emoji::Updated(),
  225. style::PaletteChanged()
  226. ) | rpl::start_with_next([=] {
  227. background->fill(Qt::transparent);
  228. // Prepare.
  229. auto p = QPainter(background);
  230. const auto height = fullSize.height();
  231. const auto fullPrintRect = QRect(QPoint(), fullPrintSize);
  232. const auto fullBarsRect = QRect(
  233. fullSize.width() - fullBarsSize.width(),
  234. 0,
  235. fullBarsSize.width(),
  236. height);
  237. const auto bigRadius = height / 2;
  238. const auto smallRadius = st::roundRadiusSmall;
  239. const auto hq = PainterHighQualityEnabler(p);
  240. p.setPen(Qt::NoPen);
  241. p.setBrush(st::callBgButton);
  242. // Fingerprint part.
  243. p.setClipRect(0, 0, fullPrintSize.width() / 2, height);
  244. p.drawRoundedRect(fullPrintRect, bigRadius, bigRadius);
  245. p.setClipRect(fullPrintSize.width() / 2, 0, fullSize.width(), height);
  246. p.drawRoundedRect(fullPrintRect, smallRadius, smallRadius);
  247. // Signal bars part.
  248. const auto middle = fullBarsRect.center().x();
  249. p.setClipRect(0, 0, middle, height);
  250. p.drawRoundedRect(fullBarsRect, smallRadius, smallRadius);
  251. p.setClipRect(middle, 0, fullBarsRect.width(), height);
  252. p.drawRoundedRect(fullBarsRect, bigRadius, bigRadius);
  253. // Emoji.
  254. const auto realSize = Ui::Emoji::GetSizeNormal();
  255. const auto size = realSize / style::DevicePixelRatio();
  256. auto left = st::callFingerprintPadding.left();
  257. const auto top = st::callFingerprintPadding.top();
  258. p.setClipping(false);
  259. for (const auto emoji : print) {
  260. Ui::Emoji::Draw(p, emoji, realSize, left, top);
  261. left += st::callFingerprintSkip + size;
  262. }
  263. raw->update();
  264. }, raw->lifetime());
  265. raw->paintRequest(
  266. ) | rpl::start_with_next([=](QRect clip) {
  267. QPainter(raw).drawImage(raw->rect(), *background);
  268. }, raw->lifetime());
  269. raw->show();
  270. return result;
  271. }
  272. } // namespace Calls