custom_emoji_instance.cpp 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989
  1. // This file is part of Desktop App Toolkit,
  2. // a set of libraries for developing nice desktop applications.
  3. //
  4. // For license and copyright information please follow this link:
  5. // https://github.com/desktop-app/legal/blob/master/LEGAL
  6. //
  7. #include "ui/text/custom_emoji_instance.h"
  8. #include "ui/effects/animation_value.h"
  9. #include "ui/effects/frame_generator.h"
  10. #include "ui/dynamic_image.h"
  11. #include "ui/ui_utility.h"
  12. #include "ui/painter.h"
  13. #include <crl/crl_async.h>
  14. #include <lz4.h>
  15. class QPainter;
  16. namespace Ui::CustomEmoji {
  17. namespace {
  18. constexpr auto kMaxFrames = 180;
  19. constexpr auto kCacheVersion = 1;
  20. constexpr auto kPreloadFrames = 3;
  21. struct CacheHeader {
  22. int version = 0;
  23. int size = 0;
  24. int frames = 0;
  25. int length = 0;
  26. };
  27. void PaintScaledImage(
  28. QPainter &p,
  29. const QRect &target,
  30. const Cache::Frame &frame,
  31. const Context &context) {
  32. static QImage PaintCache;
  33. const auto cache = context.internal.colorized ? &PaintCache : nullptr;
  34. auto q = std::optional<QPainter>();
  35. if (cache) {
  36. const auto ratio = style::DevicePixelRatio();
  37. if (cache->width() < target.width() * ratio
  38. || cache->height() < target.height() * ratio) {
  39. *cache = QImage(
  40. std::max(cache->width(), target.width() * ratio),
  41. std::max(cache->height(), target.height() * ratio),
  42. QImage::Format_ARGB32_Premultiplied);
  43. cache->setDevicePixelRatio(ratio);
  44. }
  45. q.emplace(cache);
  46. q->setCompositionMode(QPainter::CompositionMode_Source);
  47. if (context.scaled) {
  48. q->fillRect(QRect(QPoint(), target.size()), Qt::transparent);
  49. }
  50. q->translate(-target.topLeft());
  51. }
  52. const auto to = q ? &*q : &p;
  53. if (context.scaled) {
  54. const auto sx = anim::interpolate(
  55. target.width() / 2,
  56. 0,
  57. context.scale);
  58. const auto sy = (target.height() == target.width())
  59. ? sx
  60. : anim::interpolate(target.height() / 2, 0, context.scale);
  61. const auto scaled = target.marginsRemoved({ sx, sy, sx, sy });
  62. if (frame.source.isNull()) {
  63. to->drawImage(scaled, *frame.image);
  64. } else {
  65. to->drawImage(scaled, *frame.image, frame.source);
  66. }
  67. } else if (frame.source.isNull()) {
  68. to->drawImage(target, *frame.image);
  69. } else {
  70. to->drawImage(target, *frame.image, frame.source);
  71. }
  72. if (q) {
  73. q.reset();
  74. const auto ratio = style::DevicePixelRatio();
  75. const auto source = QRect(QPoint(), target.size() * ratio);
  76. const auto &color = context.textColor;
  77. style::colorizeImage(*cache, color, cache, source, {}, true);
  78. p.drawImage(target, *cache, source);
  79. }
  80. }
  81. } // namespace
  82. QColor PreviewColorFromTextColor(QColor color) {
  83. color.setAlpha((color.alpha() + 1) / 8);
  84. return color;
  85. }
  86. Preview::Preview(QPainterPath path, float64 scale)
  87. : _data(ScaledPath{ std::move(path), scale }) {
  88. }
  89. Preview::Preview(QImage image, bool exact)
  90. : _data(Image{ .data = std::move(image), .exact = exact }) {
  91. }
  92. void Preview::paint(QPainter &p, const Context &context) {
  93. if (const auto path = std::get_if<ScaledPath>(&_data)) {
  94. paintPath(p, context, *path);
  95. } else if (const auto image = std::get_if<Image>(&_data)) {
  96. const auto &data = image->data;
  97. const auto factor = style::DevicePixelRatio();
  98. const auto rect = QRect(context.position, data.size() / factor);
  99. PaintScaledImage(p, rect, { .image = &data }, context);
  100. }
  101. }
  102. bool Preview::isImage() const {
  103. return v::is<Image>(_data);
  104. }
  105. bool Preview::isExactImage() const {
  106. if (const auto image = std::get_if<Image>(&_data)) {
  107. return image->exact;
  108. }
  109. return false;
  110. }
  111. QImage Preview::image() const {
  112. if (const auto image = std::get_if<Image>(&_data)) {
  113. return image->data;
  114. }
  115. return QImage();
  116. }
  117. void Preview::paintPath(
  118. QPainter &p,
  119. const Context &context,
  120. const ScaledPath &path) {
  121. auto hq = PainterHighQualityEnabler(p);
  122. p.setBrush(PreviewColorFromTextColor(context.textColor));
  123. p.setPen(Qt::NoPen);
  124. const auto scale = path.scale;
  125. const auto required = (scale != 1.) || context.scaled;
  126. if (required) {
  127. p.save();
  128. }
  129. p.translate(context.position);
  130. if (required) {
  131. p.scale(scale, scale);
  132. const auto center = QPoint(
  133. context.size.width() / 2,
  134. context.size.height() / 2);
  135. if (context.scaled) {
  136. p.translate(center);
  137. p.scale(context.scale, context.scale);
  138. p.translate(-center);
  139. }
  140. }
  141. p.drawPath(path.path);
  142. if (required) {
  143. p.restore();
  144. } else {
  145. p.translate(-context.position);
  146. }
  147. }
  148. Cache::Cache(int size) : _size(size) {
  149. }
  150. std::optional<Cache> Cache::FromSerialized(
  151. const QByteArray &serialized,
  152. int requestedSize) {
  153. if (serialized.size() <= sizeof(CacheHeader)) {
  154. return {};
  155. }
  156. auto header = CacheHeader();
  157. memcpy(&header, serialized.data(), sizeof(header));
  158. const auto size = header.size;
  159. if (size != requestedSize
  160. || header.frames <= 0
  161. || header.frames >= kMaxFrames
  162. || header.length <= 0
  163. || header.length > (size * size * header.frames * sizeof(int32))
  164. || (serialized.size() != sizeof(CacheHeader)
  165. + header.length
  166. + (header.frames * sizeof(Cache(0)._durations[0])))) {
  167. return {};
  168. }
  169. const auto rows = (header.frames + kPerRow - 1) / kPerRow;
  170. const auto columns = std::min(header.frames, kPerRow);
  171. auto durations = std::vector<uint16>(header.frames, 0);
  172. auto full = QImage(
  173. columns * size,
  174. rows * size,
  175. QImage::Format_ARGB32_Premultiplied);
  176. Assert(full.bytesPerLine() == full.width() * sizeof(int32));
  177. const auto decompressed = LZ4_decompress_safe(
  178. serialized.data() + sizeof(CacheHeader),
  179. reinterpret_cast<char*>(full.bits()),
  180. header.length,
  181. full.bytesPerLine() * full.height());
  182. if (decompressed <= 0) {
  183. return {};
  184. }
  185. memcpy(
  186. durations.data(),
  187. serialized.data() + sizeof(CacheHeader) + header.length,
  188. header.frames * sizeof(durations[0]));
  189. auto result = Cache(size);
  190. result._finished = true;
  191. result._full = std::move(full);
  192. result._frames = header.frames;
  193. result._durations = std::move(durations);
  194. return result;
  195. }
  196. QByteArray Cache::serialize() {
  197. Expects(_finished);
  198. Expects(_durations.size() == _frames);
  199. Expects(_full.bytesPerLine() == sizeof(int32) * _full.width());
  200. auto header = CacheHeader{
  201. .version = kCacheVersion,
  202. .size = _size,
  203. .frames = _frames,
  204. };
  205. const auto input = _full.width() * _full.height() * sizeof(int32);
  206. const auto max = sizeof(CacheHeader)
  207. + LZ4_compressBound(input)
  208. + (_frames * sizeof(_durations[0]));
  209. auto result = QByteArray(max, Qt::Uninitialized);
  210. header.length = LZ4_compress_default(
  211. reinterpret_cast<const char*>(_full.constBits()),
  212. result.data() + sizeof(CacheHeader),
  213. input,
  214. result.size() - sizeof(CacheHeader));
  215. Assert(header.length > 0);
  216. memcpy(result.data(), &header, sizeof(CacheHeader));
  217. memcpy(
  218. result.data() + sizeof(CacheHeader) + header.length,
  219. _durations.data(),
  220. _frames * sizeof(_durations[0]));
  221. result.resize(sizeof(CacheHeader)
  222. + header.length
  223. + _frames * sizeof(_durations[0]));
  224. return result;
  225. }
  226. int Cache::frames() const {
  227. return _frames;
  228. }
  229. bool Cache::readyInDefaultState() const {
  230. return (_frames > 0) && !_frame;
  231. }
  232. Cache::Frame Cache::frame(int index) const {
  233. Expects(index < _frames);
  234. const auto row = index / kPerRow;
  235. const auto inrow = index % kPerRow;
  236. if (_finished) {
  237. return { &_full, { inrow * _size, row * _size, _size, _size } };
  238. }
  239. return { &_images[row], { 0, inrow * _size, _size, _size } };
  240. }
  241. int Cache::size() const {
  242. return _size;
  243. }
  244. Preview Cache::makePreview() const {
  245. Expects(_frames > 0);
  246. const auto first = frame(0);
  247. return { first.image->copy(first.source), true };
  248. }
  249. void Cache::reserve(int frames) {
  250. Expects(!_finished);
  251. const auto rows = (frames + kPerRow - 1) / kPerRow;
  252. if (const auto add = rows - int(_images.size()); add > 0) {
  253. _images.resize(rows);
  254. for (auto e = end(_images), i = e - add; i != e; ++i) {
  255. (*i) = QImage(
  256. _size,
  257. _size * kPerRow,
  258. QImage::Format_ARGB32_Premultiplied);
  259. }
  260. }
  261. _durations.reserve(frames);
  262. }
  263. int Cache::frameRowByteSize() const {
  264. return _size * 4;
  265. }
  266. int Cache::frameByteSize() const {
  267. return _size * frameRowByteSize();
  268. }
  269. void Cache::add(crl::time duration, const QImage &frame) {
  270. Expects(!_finished);
  271. Expects(frame.size() == QSize(_size, _size));
  272. Expects(frame.format() == QImage::Format_ARGB32_Premultiplied);
  273. const auto row = (_frames / kPerRow);
  274. const auto inrow = (_frames % kPerRow);
  275. const auto rows = row + 1;
  276. while (_images.size() < rows) {
  277. _images.emplace_back();
  278. _images.back() = QImage(
  279. _size,
  280. _size * kPerRow,
  281. QImage::Format_ARGB32_Premultiplied);
  282. }
  283. const auto srcPerLine = frame.bytesPerLine();
  284. const auto dstPerLine = _images[row].bytesPerLine();
  285. const auto perLine = std::min(srcPerLine, dstPerLine);
  286. auto dst = _images[row].bits() + inrow * _size * dstPerLine;
  287. auto src = frame.constBits();
  288. for (auto y = 0; y != _size; ++y) {
  289. memcpy(dst, src, perLine);
  290. dst += dstPerLine;
  291. src += srcPerLine;
  292. }
  293. ++_frames;
  294. _durations.push_back(std::clamp(
  295. duration,
  296. crl::time(0),
  297. crl::time(std::numeric_limits<uint16>::max())));
  298. }
  299. void Cache::finish() {
  300. _finished = true;
  301. if (_frame == _frames) {
  302. _frame = 0;
  303. }
  304. const auto rows = (_frames + kPerRow - 1) / kPerRow;
  305. const auto columns = std::min(_frames, kPerRow);
  306. const auto zero = (rows * columns) - _frames;
  307. _full = QImage(
  308. columns * _size,
  309. rows * _size,
  310. QImage::Format_ARGB32_Premultiplied);
  311. auto dstData = _full.bits();
  312. const auto perLine = _size * 4;
  313. const auto dstPerLine = _full.bytesPerLine();
  314. for (auto y = 0; y != rows; ++y) {
  315. auto &row = _images[y];
  316. auto src = row.bits();
  317. const auto srcPerLine = row.bytesPerLine();
  318. const auto till = columns - ((y + 1 == rows) ? zero : 0);
  319. for (auto x = 0; x != till; ++x) {
  320. auto dst = dstData + y * dstPerLine * _size + x * perLine;
  321. for (auto line = 0; line != _size; ++line) {
  322. memcpy(dst, src, perLine);
  323. src += srcPerLine;
  324. dst += dstPerLine;
  325. }
  326. }
  327. }
  328. if (const auto perLine = zero * _size) {
  329. auto dst = dstData
  330. + (rows - 1) * dstPerLine * _size
  331. + (columns - zero) * _size * 4;
  332. for (auto left = 0; left != _size; ++left) {
  333. memset(dst, 0, perLine);
  334. dst += dstPerLine;
  335. }
  336. }
  337. }
  338. PaintFrameResult Cache::paintCurrentFrame(
  339. QPainter &p,
  340. const Context &context) {
  341. if (!_frames) {
  342. return {};
  343. }
  344. const auto first = context.internal.forceFirstFrame;
  345. auto last = context.internal.forceLastFrame;
  346. if (!first && !last) {
  347. const auto now = context.paused ? 0 : context.now;
  348. const auto finishes = now ? currentFrameFinishes() : 0;
  349. if (finishes && now >= finishes) {
  350. ++_frame;
  351. if (_finished && _frame == _frames) {
  352. _frame = 0;
  353. if (context.internal.overrideFirstWithLastFrame) {
  354. last = true;
  355. }
  356. }
  357. _shown = now;
  358. } else if (!_shown) {
  359. _shown = now;
  360. }
  361. }
  362. const auto index = first
  363. ? 0
  364. : last
  365. ? (_frames - 1)
  366. : std::min(_frame, _frames - 1);
  367. const auto info = frame(index);
  368. const auto size = _size / style::DevicePixelRatio();
  369. const auto rect = QRect(context.position, QSize(size, size));
  370. PaintScaledImage(p, rect, info, context);
  371. const auto next = first ? 0 : currentFrameFinishes();
  372. return {
  373. .painted = true,
  374. .next = next,
  375. .duration = next ? (next - _shown) : 0,
  376. };
  377. }
  378. int Cache::currentFrame() const {
  379. return _frame;
  380. }
  381. crl::time Cache::currentFrameFinishes() const {
  382. if (!_shown || _frame >= _durations.size()) {
  383. return 0;
  384. } else if (const auto duration = _durations[_frame]) {
  385. return _shown + duration;
  386. }
  387. return 0;
  388. }
  389. Cached::Cached(
  390. const QString &entityData,
  391. Fn<std::unique_ptr<Loader>()> unloader,
  392. Cache cache)
  393. : _unloader(std::move(unloader))
  394. , _cache(std::move(cache))
  395. , _entityData(entityData) {
  396. }
  397. QString Cached::entityData() const {
  398. return _entityData;
  399. }
  400. PaintFrameResult Cached::paint(QPainter &p, const Context &context) {
  401. return _cache.paintCurrentFrame(p, context);
  402. }
  403. bool Cached::inDefaultState() const {
  404. return _cache.readyInDefaultState();
  405. }
  406. Preview Cached::makePreview() const {
  407. return _cache.makePreview();
  408. }
  409. Loading Cached::unload() {
  410. return Loading(_unloader(), makePreview());
  411. }
  412. Renderer::Renderer(RendererDescriptor &&descriptor)
  413. : _cache(descriptor.size)
  414. , _put(std::move(descriptor.put))
  415. , _loader(std::move(descriptor.loader)) {
  416. Expects(_loader != nullptr);
  417. const auto size = _cache.size();
  418. const auto guard = base::make_weak(this);
  419. crl::async([=, factory = std::move(descriptor.generator)]() mutable {
  420. auto generator = factory();
  421. auto rendered = generator->renderNext(
  422. QImage(),
  423. QSize(size, size),
  424. Qt::KeepAspectRatio);
  425. if (rendered.image.isNull()) {
  426. return;
  427. }
  428. crl::on_main(guard, [
  429. =,
  430. frame = std::move(rendered),
  431. generator = std::move(generator)
  432. ]() mutable {
  433. frameReady(
  434. std::move(generator),
  435. frame.duration,
  436. std::move(frame.image));
  437. });
  438. });
  439. }
  440. Renderer::~Renderer() = default;
  441. void Renderer::frameReady(
  442. std::unique_ptr<Ui::FrameGenerator> generator,
  443. crl::time duration,
  444. QImage frame) {
  445. if (frame.isNull()) {
  446. finish();
  447. return;
  448. }
  449. if (const auto count = generator->count()) {
  450. if (!_cache.frames()) {
  451. _cache.reserve(std::max(count, kMaxFrames));
  452. }
  453. }
  454. const auto current = _cache.currentFrame();
  455. const auto total = _cache.frames();
  456. const auto explicitRepaint = (current == total);
  457. _cache.add(duration, frame);
  458. if (explicitRepaint && _repaint) {
  459. _repaint();
  460. }
  461. if (!duration || total + 1 >= kMaxFrames) {
  462. finish();
  463. } else if (current + kPreloadFrames > total) {
  464. renderNext(std::move(generator), std::move(frame));
  465. } else {
  466. _generator = std::move(generator);
  467. _storage = std::move(frame);
  468. }
  469. }
  470. void Renderer::renderNext(
  471. std::unique_ptr<Ui::FrameGenerator> generator,
  472. QImage storage) {
  473. const auto size = _cache.size();
  474. const auto guard = base::make_weak(this);
  475. crl::async([
  476. =,
  477. storage = std::move(storage),
  478. generator = std::move(generator)
  479. ]() mutable {
  480. auto rendered = generator->renderNext(
  481. std::move(storage),
  482. QSize(size, size),
  483. Qt::KeepAspectRatio);
  484. crl::on_main(guard, [
  485. =,
  486. frame = std::move(rendered),
  487. generator = std::move(generator)
  488. ]() mutable {
  489. frameReady(
  490. std::move(generator),
  491. frame.duration,
  492. std::move(frame.image));
  493. });
  494. });
  495. }
  496. void Renderer::finish() {
  497. _finished = true;
  498. _cache.finish();
  499. if (_put) {
  500. _put(_cache.serialize());
  501. }
  502. }
  503. PaintFrameResult Renderer::paint(QPainter &p, const Context &context) {
  504. const auto result = _cache.paintCurrentFrame(p, context);
  505. if (_generator
  506. && (!result.painted
  507. || _cache.currentFrame() + kPreloadFrames >= _cache.frames())) {
  508. renderNext(std::move(_generator), std::move(_storage));
  509. }
  510. return result;
  511. }
  512. std::optional<Cached> Renderer::ready(const QString &entityData) {
  513. return _finished
  514. ? Cached{ entityData, std::move(_loader), std::move(_cache) }
  515. : std::optional<Cached>();
  516. }
  517. std::unique_ptr<Loader> Renderer::cancel() {
  518. return _loader();
  519. }
  520. bool Renderer::canMakePreview() const {
  521. return _cache.frames() > 0;
  522. }
  523. Preview Renderer::makePreview() const {
  524. return _cache.makePreview();
  525. }
  526. bool Renderer::readyInDefaultState() const {
  527. return _cache.readyInDefaultState();
  528. }
  529. void Renderer::setRepaintCallback(Fn<void()> repaint) {
  530. _repaint = std::move(repaint);
  531. }
  532. Cache Renderer::takeCache() {
  533. return std::move(_cache);
  534. }
  535. Loading::Loading(std::unique_ptr<Loader> loader, Preview preview)
  536. : _loader(std::move(loader))
  537. , _preview(std::move(preview)) {
  538. }
  539. QString Loading::entityData() const {
  540. return _loader->entityData();
  541. }
  542. void Loading::load(Fn<void(Loader::LoadResult)> done) {
  543. _loader->load(crl::guard(this, [this, done = std::move(done)](
  544. Loader::LoadResult result) mutable {
  545. if (const auto caching = std::get_if<Caching>(&result)) {
  546. caching->preview = _preview
  547. ? std::move(_preview)
  548. : _loader->preview();
  549. }
  550. done(std::move(result));
  551. }));
  552. }
  553. bool Loading::loading() const {
  554. return _loader->loading();
  555. }
  556. void Loading::paint(QPainter &p, const Context &context) {
  557. if (!_preview) {
  558. if (auto preview = _loader->preview()) {
  559. _preview = std::move(preview);
  560. }
  561. }
  562. _preview.paint(p, context);
  563. }
  564. bool Loading::hasImagePreview() const {
  565. return _preview.isImage();
  566. }
  567. Preview Loading::imagePreview() const {
  568. return _preview.isImage() ? _preview : Preview();
  569. }
  570. void Loading::updatePreview(Preview preview) {
  571. if (!_preview.isImage() && preview.isImage()) {
  572. _preview = std::move(preview);
  573. } else if (!_preview) {
  574. if (auto loaderPreview = _loader->preview()) {
  575. _preview = std::move(loaderPreview);
  576. } else if (preview) {
  577. _preview = std::move(preview);
  578. }
  579. }
  580. }
  581. void Loading::cancel() {
  582. _loader->cancel();
  583. invalidate_weak_ptrs(this);
  584. }
  585. Instance::Instance(
  586. Loading loading,
  587. Fn<void(not_null<Instance*>, RepaintRequest)> repaintLater)
  588. : _state(std::move(loading))
  589. , _repaintLater(std::move(repaintLater)) {
  590. }
  591. QString Instance::entityData() const {
  592. return v::match(_state, [](const Loading &state) {
  593. return state.entityData();
  594. }, [](const Caching &state) {
  595. return state.entityData;
  596. }, [](const Cached &state) {
  597. return state.entityData();
  598. });
  599. }
  600. void Instance::paint(QPainter &p, const Context &context) {
  601. context.internal.colorized = _colored;
  602. v::match(_state, [&](Loading &state) {
  603. state.paint(p, context);
  604. load(state);
  605. }, [&](Caching &state) {
  606. auto result = state.renderer->paint(p, context);
  607. if (!result.painted) {
  608. state.preview.paint(p, context);
  609. } else {
  610. if (!state.preview.isExactImage()) {
  611. state.preview = state.renderer->makePreview();
  612. }
  613. if (result.next > context.now) {
  614. _repaintLater(this, { result.next, result.duration });
  615. }
  616. }
  617. if (auto cached = state.renderer->ready(state.entityData)) {
  618. _state = std::move(*cached);
  619. }
  620. }, [&](Cached &state) {
  621. const auto result = state.paint(p, context);
  622. if (result.next > context.now) {
  623. _repaintLater(this, { result.next, result.duration });
  624. }
  625. });
  626. }
  627. bool Instance::ready() {
  628. return v::match(_state, [&](Loading &state) {
  629. if (state.hasImagePreview()) {
  630. return true;
  631. }
  632. if (!_usage.empty()) {
  633. load(state);
  634. }
  635. return false;
  636. }, [](Caching &state) {
  637. return state.renderer->canMakePreview();
  638. }, [](Cached &state) {
  639. return true;
  640. });
  641. }
  642. bool Instance::readyInDefaultState() {
  643. return v::match(_state, [&](Loading &state) {
  644. if (state.hasImagePreview()) {
  645. return true;
  646. }
  647. load(state);
  648. return false;
  649. }, [](Caching &state) {
  650. return state.renderer->readyInDefaultState();
  651. }, [](Cached &state) {
  652. return state.inDefaultState();
  653. });
  654. }
  655. void Instance::load(Loading &state) {
  656. state.load([=](Loader::LoadResult result) {
  657. if (auto caching = std::get_if<Caching>(&result)) {
  658. caching->renderer->setRepaintCallback([=] { repaint(); });
  659. _state = std::move(*caching);
  660. } else if (auto cached = std::get_if<Cached>(&result)) {
  661. _state = std::move(*cached);
  662. repaint();
  663. } else {
  664. Unexpected("Value in Loader::LoadResult.");
  665. }
  666. });
  667. }
  668. bool Instance::hasImagePreview() const {
  669. return v::match(_state, [](const Loading &state) {
  670. return state.hasImagePreview();
  671. }, [](const Caching &state) {
  672. return state.preview.isImage();
  673. }, [](const Cached &state) {
  674. return true;
  675. });
  676. }
  677. Preview Instance::imagePreview() const {
  678. return v::match(_state, [](const Loading &state) {
  679. return state.imagePreview();
  680. }, [](const Caching &state) {
  681. return state.preview.isImage() ? state.preview : Preview();
  682. }, [](const Cached &state) {
  683. return state.makePreview();
  684. });
  685. }
  686. void Instance::updatePreview(Preview preview) {
  687. v::match(_state, [&](Loading &state) {
  688. state.updatePreview(std::move(preview));
  689. }, [&](Caching &state) {
  690. if ((!state.preview.isImage() && preview.isImage())
  691. || (!state.preview && preview)) {
  692. state.preview = std::move(preview);
  693. }
  694. }, [](const Cached &) {});
  695. }
  696. void Instance::setColored() {
  697. if (!_colored) {
  698. _colored = true;
  699. if (ready()) {
  700. _repaintLater(this, { .when = crl::now() + 1 });
  701. }
  702. }
  703. }
  704. void Instance::repaint() {
  705. for (const auto &object : _usage) {
  706. object->repaint();
  707. }
  708. }
  709. void Instance::incrementUsage(not_null<Object*> object) {
  710. _usage.emplace(object);
  711. }
  712. void Instance::decrementUsage(not_null<Object*> object) {
  713. _usage.remove(object);
  714. if (!_usage.empty()) {
  715. return;
  716. }
  717. v::match(_state, [](Loading &state) {
  718. state.cancel();
  719. }, [&](Caching &state) {
  720. _state = Loading{
  721. state.renderer->cancel(),
  722. std::move(state.preview),
  723. };
  724. }, [&](Cached &state) {
  725. _state = state.unload();
  726. });
  727. _repaintLater(this, RepaintRequest());
  728. }
  729. Object::Object(not_null<Instance*> instance, Fn<void()> repaint)
  730. : _instance(instance)
  731. , _repaint(std::move(repaint)) {
  732. }
  733. Object::~Object() {
  734. unload();
  735. }
  736. int Object::width() {
  737. return st::emojiSize + 2 * st::emojiPadding;
  738. }
  739. QString Object::entityData() {
  740. return _instance->entityData();
  741. }
  742. void Object::paint(QPainter &p, const Context &context) {
  743. if (!_using) {
  744. _using = true;
  745. _instance->incrementUsage(this);
  746. }
  747. _instance->paint(p, context);
  748. }
  749. void Object::unload() {
  750. if (_using) {
  751. _using = false;
  752. _instance->decrementUsage(this);
  753. }
  754. }
  755. bool Object::ready() {
  756. if (!_using) {
  757. _using = true;
  758. _instance->incrementUsage(this);
  759. }
  760. return _instance->ready();
  761. }
  762. bool Object::readyInDefaultState() {
  763. if (!_using) {
  764. _using = true;
  765. _instance->incrementUsage(this);
  766. }
  767. return _instance->readyInDefaultState();
  768. }
  769. void Object::repaint() {
  770. if (const auto onstack = _repaint) {
  771. onstack();
  772. }
  773. }
  774. Internal::Internal(
  775. QString entityData,
  776. QImage image,
  777. QMargins padding,
  778. bool colored)
  779. : _entityData(std::move(entityData))
  780. , _image(std::move(image))
  781. , _padding(padding)
  782. , _colored(colored) {
  783. }
  784. int Internal::width() {
  785. return _padding.left()
  786. + (_image.width() / _image.devicePixelRatio())
  787. + _padding.right();
  788. }
  789. QString Internal::entityData() {
  790. return _entityData;
  791. }
  792. void Internal::paint(QPainter &p, const Context &context) {
  793. context.internal.colorized = _colored;
  794. const auto size = _image.size() / style::DevicePixelRatio();
  795. const auto rect = QRect(
  796. context.position + QPoint(_padding.left(), _padding.top()),
  797. size);
  798. PaintScaledImage(p, rect, { &_image }, context);
  799. }
  800. void Internal::unload() {
  801. }
  802. bool Internal::ready() {
  803. return true;
  804. }
  805. bool Internal::readyInDefaultState() {
  806. return true;
  807. }
  808. DynamicImageEmoji::DynamicImageEmoji(
  809. QString entityData,
  810. std::shared_ptr<DynamicImage> image,
  811. Fn<void()> repaint,
  812. QMargins padding,
  813. int size)
  814. : _entityData(entityData)
  815. , _image(std::move(image))
  816. , _repaint(std::move(repaint))
  817. , _padding(padding)
  818. , _size(size) {
  819. }
  820. int DynamicImageEmoji::width() {
  821. return _padding.left() + _size + _padding.right();
  822. }
  823. QString DynamicImageEmoji::entityData() {
  824. return _entityData;
  825. }
  826. void DynamicImageEmoji::paint(QPainter &p, const Context &context) {
  827. if (!_subscribed) {
  828. _subscribed = true;
  829. _image->subscribeToUpdates(_repaint);
  830. }
  831. auto image = _image->image(_size);
  832. const auto size = image.size() / image.devicePixelRatio();
  833. const auto rect = QRect(
  834. context.position + QPoint(_padding.left(), _padding.top()),
  835. size);
  836. context.internal.colorized = false;
  837. PaintScaledImage(p, rect, { &image }, context);
  838. }
  839. void DynamicImageEmoji::unload() {
  840. if (_subscribed) {
  841. _subscribed = false;
  842. _image->subscribeToUpdates(nullptr);
  843. }
  844. }
  845. bool DynamicImageEmoji::ready() {
  846. return true;
  847. }
  848. bool DynamicImageEmoji::readyInDefaultState() {
  849. return true;
  850. }
  851. void PaintIconEmoji(
  852. QPainter &p,
  853. const Context &context,
  854. not_null<const style::IconEmoji*> emoji,
  855. IconEmojiFrameCache &cache) {
  856. context.internal.colorized = !emoji->useIconColor;
  857. const auto size = emoji->icon.size();
  858. const auto rect = QRect(context.position
  859. + QPoint(emoji->padding.left(), emoji->padding.top()), size);
  860. const auto ratio = style::DevicePixelRatio();
  861. const auto full = size * ratio;
  862. const auto invalid = (cache.frame.size() != full);
  863. if (invalid
  864. || (emoji->useIconColor
  865. && cache.paletteVersion != style::PaletteVersion())) {
  866. cache.paletteVersion = style::PaletteVersion();
  867. if (invalid) {
  868. cache.frame = QImage(full, QImage::Format_ARGB32_Premultiplied);
  869. }
  870. cache.frame.setDevicePixelRatio(ratio);
  871. cache.frame.fill(Qt::transparent);
  872. auto q = QPainter(&cache.frame);
  873. emoji->icon.paint(q, 0, 0, size.width());
  874. }
  875. PaintScaledImage(p, rect, { &cache.frame }, context);
  876. }
  877. } // namespace Ui::CustomEmoji