lottie.cpp 8.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264
  1. #include <jni.h>
  2. #include <android/bitmap.h>
  3. #include <cstring>
  4. #include <rlottie.h>
  5. #include <lz4.h>
  6. #include <unistd.h>
  7. #include <condition_variable>
  8. #include <atomic>
  9. #include <thread>
  10. #include <map>
  11. #include <sys/stat.h>
  12. #include <utime.h>
  13. #include "tgnet/FileLog.h"
  14. #include "tgnet/ConnectionsManager.h"
  15. #include "c_utils.h"
  16. extern "C" {
  17. using namespace rlottie;
  18. typedef struct LottieInfo {
  19. ~LottieInfo() {
  20. if (decompressBuffer != nullptr) {
  21. delete[]decompressBuffer;
  22. decompressBuffer = nullptr;
  23. }
  24. }
  25. std::unique_ptr<Animation> animation;
  26. size_t frameCount = 0;
  27. int32_t fps = 30;
  28. bool precache = false;
  29. bool createCache = false;
  30. bool limitFps = false;
  31. std::string path;
  32. std::string cacheFile;
  33. uint8_t *decompressBuffer = nullptr;
  34. uint32_t decompressBufferSize = 0;
  35. volatile uint32_t maxFrameSize = 0;
  36. uint32_t imageSize = 0;
  37. uint32_t fileOffset = 0;
  38. uint32_t fileFrame = 0;
  39. bool nextFrameIsCacheFrame = false;
  40. char *compressBuffer = nullptr;
  41. const char *buffer = nullptr;
  42. bool firstFrame = false;
  43. int bufferSize = 0;
  44. int compressBound = 0;
  45. int firstFrameSize = 0;
  46. volatile uint32_t framesAvailableInCache = 0;
  47. };
  48. JNIEXPORT jlong Java_org_telegram_ui_Components_RLottieDrawable_create(JNIEnv *env, jclass clazz, jstring src, jstring json, jint w, jint h, jintArray data, jboolean precache, jintArray colorReplacement, jboolean limitFps, jint fitzModifier) {
  49. auto info = new LottieInfo();
  50. std::map<int32_t, int32_t> *colors = nullptr;
  51. int color = 0;
  52. if (colorReplacement != nullptr) {
  53. jint *arr = env->GetIntArrayElements(colorReplacement, nullptr);
  54. if (arr != nullptr) {
  55. jsize len = env->GetArrayLength(colorReplacement);
  56. colors = new std::map<int32_t, int32_t>();
  57. for (int32_t a = 0; a < len / 2; a++) {
  58. (*colors)[arr[a * 2]] = arr[a * 2 + 1];
  59. if (color == 0) {
  60. color = arr[a * 2 + 1];
  61. }
  62. }
  63. env->ReleaseIntArrayElements(colorReplacement, arr, 0);
  64. }
  65. }
  66. FitzModifier modifier = FitzModifier::None;
  67. switch (fitzModifier) {
  68. case 12:
  69. modifier = FitzModifier::Type12;
  70. break;
  71. case 3:
  72. modifier = FitzModifier::Type3;
  73. break;
  74. case 4:
  75. modifier = FitzModifier::Type4;
  76. break;
  77. case 5:
  78. modifier = FitzModifier::Type5;
  79. break;
  80. case 6:
  81. modifier = FitzModifier::Type6;
  82. break;
  83. }
  84. char const *srcString = env->GetStringUTFChars(src, nullptr);
  85. info->path = srcString;
  86. if (json != nullptr) {
  87. char const *jsonString = env->GetStringUTFChars(json, nullptr);
  88. if (jsonString) {
  89. info->animation = rlottie::Animation::loadFromData(jsonString, info->path, colors, modifier);
  90. env->ReleaseStringUTFChars(json, jsonString);
  91. }
  92. } else {
  93. info->animation = rlottie::Animation::loadFromFile(info->path, colors, modifier);
  94. }
  95. if (srcString) {
  96. env->ReleaseStringUTFChars(src, srcString);
  97. }
  98. if (info->animation == nullptr) {
  99. delete info;
  100. return 0;
  101. }
  102. info->frameCount = info->animation->totalFrame();
  103. info->fps = (int) info->animation->frameRate();
  104. info->limitFps = limitFps;
  105. if (info->fps > 60 || info->frameCount > 600) {
  106. delete info;
  107. return 0;
  108. }
  109. info->precache = precache;
  110. if (info->precache) {
  111. info->cacheFile = info->path;
  112. std::string::size_type index = info->cacheFile.find_last_of('/');
  113. if (index != std::string::npos) {
  114. std::string dir = info->cacheFile.substr(0, index) + "/acache";
  115. mkdir(dir.c_str(), 0777);
  116. info->cacheFile.insert(index, "/acache");
  117. }
  118. info->cacheFile += std::to_string(w) + "_" + std::to_string(h);
  119. if (color != 0) {
  120. info->cacheFile += "_" + std::to_string(color);
  121. }
  122. if (limitFps) {
  123. info->cacheFile += ".s.cache";
  124. } else {
  125. info->cacheFile += ".cache";
  126. }
  127. FILE *precacheFile = fopen(info->cacheFile.c_str(), "r+");
  128. if (precacheFile == nullptr) {
  129. info->createCache = true;
  130. } else {
  131. uint8_t temp;
  132. size_t read = fread(&temp, sizeof(uint8_t), 1, precacheFile);
  133. info->createCache = read != 1 || temp == 0;
  134. if (!info->createCache) {
  135. uint32_t maxFrameSize;
  136. fread(&maxFrameSize, sizeof(uint32_t), 1, precacheFile);
  137. info->maxFrameSize = maxFrameSize;
  138. fread(&(info->imageSize), sizeof(uint32_t), 1, precacheFile);
  139. info->fileOffset = 9;
  140. info->fileFrame = 0;
  141. utimensat(0, info->cacheFile.c_str(), nullptr, 0);
  142. }
  143. fclose(precacheFile);
  144. }
  145. }
  146. jint *dataArr = env->GetIntArrayElements(data, nullptr);
  147. if (dataArr != nullptr) {
  148. dataArr[0] = (jint) info->frameCount;
  149. dataArr[1] = (jint) info->animation->frameRate();
  150. dataArr[2] = info->createCache ? 1 : 0;
  151. env->ReleaseIntArrayElements(data, dataArr, 0);
  152. }
  153. return (jlong) (intptr_t) info;
  154. }
  155. JNIEXPORT jlong Java_org_telegram_ui_Components_RLottieDrawable_createWithJson(JNIEnv *env, jclass clazz, jstring json, jstring name, jintArray data, jintArray colorReplacement) {
  156. std::map<int32_t, int32_t> *colors = nullptr;
  157. if (colorReplacement != nullptr) {
  158. jint *arr = env->GetIntArrayElements(colorReplacement, nullptr);
  159. if (arr != nullptr) {
  160. jsize len = env->GetArrayLength(colorReplacement);
  161. colors = new std::map<int32_t, int32_t>();
  162. for (int32_t a = 0; a < len / 2; a++) {
  163. (*colors)[arr[a * 2]] = arr[a * 2 + 1];
  164. }
  165. env->ReleaseIntArrayElements(colorReplacement, arr, 0);
  166. }
  167. }
  168. auto info = new LottieInfo();
  169. char const *jsonString = env->GetStringUTFChars(json, nullptr);
  170. char const *nameString = env->GetStringUTFChars(name, nullptr);
  171. info->animation = rlottie::Animation::loadFromData(jsonString, nameString, colors);
  172. if (jsonString) {
  173. env->ReleaseStringUTFChars(json, jsonString);
  174. }
  175. if (nameString) {
  176. env->ReleaseStringUTFChars(name, nameString);
  177. }
  178. if (info->animation == nullptr) {
  179. delete info;
  180. return 0;
  181. }
  182. info->frameCount = info->animation->totalFrame();
  183. info->fps = (int) info->animation->frameRate();
  184. jint *dataArr = env->GetIntArrayElements(data, nullptr);
  185. if (dataArr != nullptr) {
  186. dataArr[0] = (int) info->frameCount;
  187. dataArr[1] = (int) info->animation->frameRate();
  188. dataArr[2] = 0;
  189. env->ReleaseIntArrayElements(data, dataArr, 0);
  190. }
  191. return (jlong) (intptr_t) info;
  192. }
  193. JNIEXPORT void Java_org_telegram_ui_Components_RLottieDrawable_destroy(JNIEnv *env, jclass clazz, jlong ptr) {
  194. if (!ptr) {
  195. return;
  196. }
  197. auto info = (LottieInfo *) (intptr_t) ptr;
  198. delete info;
  199. }
  200. JNIEXPORT void Java_org_telegram_ui_Components_RLottieDrawable_setLayerColor(JNIEnv *env, jclass clazz, jlong ptr, jstring layer, jint color) {
  201. if (!ptr || layer == nullptr) {
  202. return;
  203. }
  204. auto info = (LottieInfo *) (intptr_t) ptr;
  205. char const *layerString = env->GetStringUTFChars(layer, nullptr);
  206. info->animation->setValue<Property::Color>(layerString, Color(((color) & 0xff) / 255.0f, ((color >> 8) & 0xff) / 255.0f, ((color >> 16) & 0xff) / 255.0f));
  207. if (layerString) {
  208. env->ReleaseStringUTFChars(layer, layerString);
  209. }
  210. }
  211. JNIEXPORT void Java_org_telegram_ui_Components_RLottieDrawable_replaceColors(JNIEnv *env, jclass clazz, jlong ptr, jintArray colorReplacement) {
  212. if (!ptr || colorReplacement == nullptr) {
  213. return;
  214. }
  215. auto info = (LottieInfo *) (intptr_t) ptr;
  216. jint *arr = env->GetIntArrayElements(colorReplacement, nullptr);
  217. if (arr != nullptr) {
  218. jsize len = env->GetArrayLength(colorReplacement);
  219. for (int32_t a = 0; a < len / 2; a++) {
  220. (*info->animation->colorMap)[arr[a * 2]] = arr[a * 2 + 1];
  221. }
  222. info->animation->resetCurrentFrame();
  223. env->ReleaseIntArrayElements(colorReplacement, arr, 0);
  224. }
  225. }
  226. JNIEXPORT jint Java_org_telegram_ui_Components_RLottieDrawable_getFrame(JNIEnv *env, jclass clazz, jlong ptr, jint frame, jobject bitmap, jint w, jint h, jint stride, jboolean clear) {
  227. if (!ptr || bitmap == nullptr) {
  228. return 0;
  229. }
  230. auto info = (LottieInfo *) (intptr_t) ptr;
  231. void *pixels;
  232. bool result = false;
  233. if (AndroidBitmap_lockPixels(env, bitmap, &pixels) >= 0) {
  234. Surface surface((uint32_t *) pixels, (size_t) w, (size_t) h, (size_t) stride);
  235. info->animation->renderSync((size_t) frame, surface, clear, &result);
  236. AndroidBitmap_unlockPixels(env, bitmap);
  237. }
  238. if (!result) {
  239. return -5;
  240. }
  241. return frame;
  242. }
  243. }