lottie_animation.cpp 8.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334
  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 "lottie/lottie_animation.h"
  8. #include "lottie/details/lottie_frame_renderer.h"
  9. #include "lottie/details/lottie_frame_provider_direct.h"
  10. #include "lottie/lottie_player.h"
  11. #include "ui/image/image_prepare.h"
  12. #include "base/algorithm.h"
  13. #include "base/assertion.h"
  14. #include "base/variant.h"
  15. #ifdef LOTTIE_USE_CACHE
  16. #include "lottie/details/lottie_frame_provider_cached.h"
  17. #include "lottie/details/lottie_frame_provider_cached_multi.h"
  18. #endif // LOTTIE_USE_CACHE
  19. #include <QFile>
  20. #include <rlottie.h>
  21. #include <crl/crl_async.h>
  22. #include <crl/crl_on_main.h>
  23. namespace Lottie {
  24. namespace {
  25. const auto kIdealSize = QSize(512, 512);
  26. details::InitData CheckSharedState(std::unique_ptr<SharedState> state) {
  27. Expects(state != nullptr);
  28. auto information = state->information();
  29. if (!information.frameRate
  30. || information.framesCount <= 0
  31. || information.size.isEmpty()) {
  32. return Error::NotSupported;
  33. }
  34. return state;
  35. }
  36. details::InitData Init(
  37. const QByteArray &content,
  38. const FrameRequest &request,
  39. Quality quality,
  40. const ColorReplacements *replacements) {
  41. if (const auto error = ContentError(content)) {
  42. return *error;
  43. }
  44. auto provider = std::make_shared<FrameProviderDirect>(quality);
  45. if (!provider->load(content, replacements)) {
  46. return Error::ParseFailed;
  47. }
  48. return CheckSharedState(std::make_unique<SharedState>(
  49. std::move(provider),
  50. request.empty() ? FrameRequest{ kIdealSize } : request));
  51. }
  52. #ifdef LOTTIE_USE_CACHE
  53. details::InitData Init(
  54. const QByteArray &content,
  55. FnMut<void(QByteArray &&cached)> put,
  56. const QByteArray &cached,
  57. const FrameRequest &request,
  58. Quality quality,
  59. const ColorReplacements *replacements) {
  60. Expects(!request.empty());
  61. if (const auto error = ContentError(content)) {
  62. return *error;
  63. }
  64. auto provider = std::make_shared<FrameProviderCached>(
  65. content,
  66. std::move(put),
  67. cached,
  68. request,
  69. quality,
  70. replacements);
  71. return provider->valid()
  72. ? CheckSharedState(std::make_unique<SharedState>(
  73. std::move(provider),
  74. request.empty() ? FrameRequest{ kIdealSize } : request))
  75. : Error::ParseFailed;
  76. }
  77. details::InitData Init(
  78. const QByteArray &content,
  79. FnMut<void(int, QByteArray &&cached)> put,
  80. std::vector<QByteArray> caches,
  81. const FrameRequest &request,
  82. Quality quality,
  83. const ColorReplacements *replacements) {
  84. Expects(!request.empty());
  85. if (const auto error = ContentError(content)) {
  86. return *error;
  87. }
  88. auto provider = std::make_shared<FrameProviderCachedMulti>(
  89. content,
  90. std::move(put),
  91. std::move(caches),
  92. request,
  93. quality,
  94. replacements);
  95. return provider->valid()
  96. ? CheckSharedState(std::make_unique<SharedState>(
  97. std::move(provider),
  98. request.empty() ? FrameRequest{ kIdealSize } : request))
  99. : Error::ParseFailed;
  100. }
  101. #endif // LOTTIE_USE_CACHE
  102. details::InitData Init(
  103. std::shared_ptr<FrameProvider> provider,
  104. const FrameRequest &request) {
  105. Expects(!request.empty());
  106. return provider->valid()
  107. ? CheckSharedState(std::make_unique<SharedState>(
  108. std::move(provider),
  109. request.empty() ? FrameRequest{ kIdealSize } : request))
  110. : Error::ParseFailed;
  111. }
  112. } // namespace
  113. std::shared_ptr<FrameRenderer> MakeFrameRenderer() {
  114. return FrameRenderer::CreateIndependent();
  115. }
  116. QImage ReadThumbnail(const QByteArray &content) {
  117. return v::match(Init(content, FrameRequest(), Quality::High, nullptr), [](
  118. const std::unique_ptr<SharedState> &state) {
  119. return state->frameForPaint()->original;
  120. }, [](Error) {
  121. return QImage();
  122. });
  123. }
  124. Animation::Animation(
  125. not_null<Player*> player,
  126. const QByteArray &content,
  127. const FrameRequest &request,
  128. Quality quality,
  129. const ColorReplacements *replacements)
  130. : _player(player) {
  131. if (quality == Quality::Synchronous) {
  132. initDone(Init(content, request, quality, replacements));
  133. } else {
  134. const auto weak = base::make_weak(this);
  135. crl::async([=] {
  136. auto result = Init(content, request, quality, replacements);
  137. crl::on_main(weak, [=, data = std::move(result)]() mutable {
  138. initDone(std::move(data));
  139. });
  140. });
  141. }
  142. }
  143. Animation::Animation(
  144. not_null<Player*> player,
  145. FnMut<void(FnMut<void(QByteArray &&cached)>)> get, // Main thread.
  146. FnMut<void(QByteArray &&cached)> put, // Unknown thread.
  147. const QByteArray &content,
  148. const FrameRequest &request,
  149. Quality quality,
  150. const ColorReplacements *replacements)
  151. #ifdef LOTTIE_USE_CACHE
  152. : _player(player) {
  153. const auto weak = base::make_weak(this);
  154. get([=, put = std::move(put)](QByteArray &&cached) mutable {
  155. crl::async([=, put = std::move(put)]() mutable {
  156. auto result = Init(
  157. content,
  158. std::move(put),
  159. cached,
  160. request,
  161. quality,
  162. replacements);
  163. crl::on_main(weak, [=, data = std::move(result)]() mutable {
  164. initDone(std::move(data));
  165. });
  166. });
  167. });
  168. #else // LOTTIE_USE_CACHE
  169. : Animation(player, content, request, quality, replacements) {
  170. #endif // LOTTIE_USE_CACHE
  171. }
  172. Animation::Animation(
  173. not_null<Player*> player,
  174. int keysCount,
  175. FnMut<void(int, FnMut<void(QByteArray &&)>)> get,
  176. FnMut<void(int, QByteArray &&cached)> put,
  177. const QByteArray &content,
  178. const FrameRequest &request,
  179. Quality quality,
  180. const ColorReplacements *replacements)
  181. #ifdef LOTTIE_USE_CACHE
  182. : _player(player) {
  183. const auto weak = base::make_weak(this);
  184. struct State {
  185. std::atomic<int> left = 0;
  186. std::vector<QByteArray> caches;
  187. FnMut<void(int, QByteArray &&cached)> put;
  188. };
  189. const auto state = std::make_shared<State>();
  190. state->left = keysCount;
  191. state->put = std::move(put);
  192. state->caches.resize(keysCount);
  193. for (auto i = 0; i != keysCount; ++i) {
  194. get(i, [=](QByteArray &&cached) {
  195. state->caches[i] = std::move(cached);
  196. if (--state->left) {
  197. return;
  198. }
  199. crl::async([=] {
  200. auto result = Init(
  201. content,
  202. std::move(state->put),
  203. std::move(state->caches),
  204. request,
  205. quality,
  206. replacements);
  207. crl::on_main(weak, [=, data = std::move(result)]() mutable {
  208. initDone(std::move(data));
  209. });
  210. });
  211. });
  212. }
  213. #else // LOTTIE_USE_CACHE
  214. : Animation(player, content, request, quality, replacements) {
  215. #endif // LOTTIE_USE_CACHE
  216. }
  217. Animation::Animation(
  218. not_null<Player*> player,
  219. std::shared_ptr<FrameProvider> provider,
  220. const FrameRequest &request)
  221. : _player(player) {
  222. const auto weak = base::make_weak(this);
  223. crl::async([=, provider = std::move(provider)]() mutable {
  224. auto result = Init(std::move(provider), request);
  225. crl::on_main(weak, [=, data = std::move(result)]() mutable {
  226. initDone(std::move(data));
  227. });
  228. });
  229. }
  230. bool Animation::ready() const {
  231. return (_state != nullptr);
  232. }
  233. void Animation::initDone(details::InitData &&data) {
  234. v::match(data, [&](std::unique_ptr<SharedState> &state) {
  235. parseDone(std::move(state));
  236. }, [&](Error error) {
  237. parseFailed(error);
  238. });
  239. }
  240. void Animation::parseDone(std::unique_ptr<SharedState> state) {
  241. Expects(state != nullptr);
  242. _state = state.get();
  243. _player->start(this, std::move(state));
  244. }
  245. void Animation::parseFailed(Error error) {
  246. _player->failed(this, error);
  247. }
  248. QImage Animation::frame() const {
  249. Expects(_state != nullptr);
  250. return PrepareFrameByRequest(_state->frameForPaint(), true);
  251. }
  252. QImage Animation::frame(const FrameRequest &request) const {
  253. Expects(_state != nullptr);
  254. const auto frame = _state->frameForPaint();
  255. const auto changed = (frame->request != request);
  256. if (changed) {
  257. frame->request = request;
  258. _player->updateFrameRequest(this, request);
  259. }
  260. return PrepareFrameByRequest(frame, !changed);
  261. }
  262. auto Animation::frameInfo(const FrameRequest &request) const -> FrameInfo {
  263. Expects(_state != nullptr);
  264. const auto frame = _state->frameForPaint();
  265. const auto changed = (frame->request != request);
  266. if (changed) {
  267. frame->request = request;
  268. _player->updateFrameRequest(this, request);
  269. }
  270. return {
  271. PrepareFrameByRequest(frame, !changed),
  272. frame->index % _state->framesCount()
  273. };
  274. }
  275. int Animation::frameIndex() const {
  276. Expects(_state != nullptr);
  277. const auto frame = _state->frameForPaint();
  278. return frame->index % _state->framesCount();
  279. }
  280. int Animation::framesCount() const {
  281. Expects(_state != nullptr);
  282. return _state->framesCount();
  283. }
  284. Information Animation::information() const {
  285. Expects(_state != nullptr);
  286. return _state->information();
  287. }
  288. std::optional<Error> ContentError(const QByteArray &content) {
  289. if (content.size() > kMaxFileSize) {
  290. return Error::ParseFailed;
  291. }
  292. return std::nullopt;
  293. }
  294. } // namespace Lottie