aboutsummaryrefslogtreecommitdiffstats
path: root/src/main/jni/gif.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/main/jni/gif.c')
-rw-r--r--src/main/jni/gif.c847
1 files changed, 847 insertions, 0 deletions
diff --git a/src/main/jni/gif.c b/src/main/jni/gif.c
new file mode 100644
index 000000000..64dda4793
--- /dev/null
+++ b/src/main/jni/gif.c
@@ -0,0 +1,847 @@
+//thanks to https://github.com/koral--/android-gif-drawable
+/*
+ MIT License
+ Copyright (c)
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to deal
+ in the Software without restriction, including without limitation the rights
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ copies of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included in
+ all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ THE SOFTWARE.
+
+ // Copyright (c) 2011 Google Inc. All rights reserved.
+ //
+ // Redistribution and use in source and binary forms, with or without
+ // modification, are permitted provided that the following conditions are
+ // met:
+ //
+ // * Redistributions of source code must retain the above copyright
+ // notice, this list of conditions and the following disclaimer.
+ // * Redistributions in binary form must reproduce the above
+ // copyright notice, this list of conditions and the following disclaimer
+ // in the documentation and/or other materials provided with the
+ // distribution.
+ // * Neither the name of Google Inc. nor the names of its
+ // contributors may be used to endorse or promote products derived from
+ // this software without specific prior written permission.
+ //
+ // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+ The GIFLIB distribution is Copyright (c) 1997 Eric S. Raymond
+ */
+
+#include <jni.h>
+#include <stdio.h>
+#include <time.h>
+#include <limits.h>
+#include "gif.h"
+#include "giflib/gif_lib.h"
+
+#define D_GIF_ERR_NO_FRAMES 1000
+#define D_GIF_ERR_INVALID_SCR_DIMS 1001
+#define D_GIF_ERR_INVALID_IMG_DIMS 1002
+#define D_GIF_ERR_IMG_NOT_CONFINED 1003
+
+typedef struct {
+ uint8_t blue;
+ uint8_t green;
+ uint8_t red;
+ uint8_t alpha;
+} argb;
+
+typedef struct {
+ unsigned int duration;
+ int transpIndex;
+ unsigned char disposalMethod;
+} FrameInfo;
+
+typedef struct {
+ GifFileType *gifFilePtr;
+ unsigned long lastFrameReaminder;
+ unsigned long nextStartTime;
+ int currentIndex;
+ unsigned int lastDrawIndex;
+ FrameInfo *infos;
+ argb *backupPtr;
+ int startPos;
+ unsigned char *rasterBits;
+ char *comment;
+ unsigned short loopCount;
+ int currentLoop;
+ jfloat speedFactor;
+} GifInfo;
+
+static ColorMapObject *defaultCmap = NULL;
+
+static ColorMapObject *genDefColorMap(void) {
+ ColorMapObject *cmap = GifMakeMapObject(256, NULL);
+ if (cmap != NULL) {
+ int iColor;
+ for (iColor = 0; iColor < 256; iColor++) {
+ cmap->Colors[iColor].Red = (GifByteType) iColor;
+ cmap->Colors[iColor].Green = (GifByteType) iColor;
+ cmap->Colors[iColor].Blue = (GifByteType) iColor;
+ }
+ }
+ return cmap;
+}
+
+jint gifOnJNILoad(JavaVM *vm, void *reserved, JNIEnv *env) {
+ defaultCmap = genDefColorMap();
+ if (defaultCmap == NULL) {
+ return -1;
+ }
+ return JNI_VERSION_1_6;
+}
+
+void gifOnJNIUnload(JavaVM *vm, void *reserved) {
+ GifFreeMapObject(defaultCmap);
+}
+
+static int fileReadFunc(GifFileType *gif, GifByteType *bytes, int size) {
+ FILE *file = (FILE *)gif->UserData;
+ return fread(bytes, 1, size, file);
+}
+
+static int fileRewindFun(GifInfo *info) {
+ return fseek(info->gifFilePtr->UserData, info->startPos, SEEK_SET);
+}
+
+static unsigned long getRealTime() {
+ struct timespec ts;
+ const clockid_t id = CLOCK_MONOTONIC;
+ if (id != (clockid_t) - 1 && clock_gettime(id, &ts) != -1) {
+ return ts.tv_sec * 1000 + ts.tv_nsec / 1000000;
+ }
+ return -1;
+}
+
+static void cleanUp(GifInfo *info) {
+ if (info->backupPtr) {
+ free(info->backupPtr);
+ info->backupPtr = NULL;
+ }
+ if (info->infos) {
+ free(info->infos);
+ info->infos = NULL;
+ }
+ if (info->rasterBits) {
+ free(info->rasterBits);
+ info->rasterBits = NULL;
+ }
+ if (info->comment) {
+ free(info->comment);
+ info->comment = NULL;
+ }
+
+ GifFileType *GifFile = info->gifFilePtr;
+ if (GifFile->SColorMap == defaultCmap) {
+ GifFile->SColorMap = NULL;
+ }
+ if (GifFile->SavedImages != NULL) {
+ SavedImage *sp;
+ for (sp = GifFile->SavedImages; sp < GifFile->SavedImages + GifFile->ImageCount; sp++) {
+ if (sp->ImageDesc.ColorMap != NULL) {
+ GifFreeMapObject(sp->ImageDesc.ColorMap);
+ sp->ImageDesc.ColorMap = NULL;
+ }
+ }
+ free(GifFile->SavedImages);
+ GifFile->SavedImages = NULL;
+ }
+ DGifCloseFile(GifFile);
+ free(info);
+}
+
+static int getComment(GifByteType *Bytes, char **cmt) {
+ unsigned int len = (unsigned int) Bytes[0];
+ unsigned int offset = *cmt != NULL ? strlen(*cmt) : 0;
+ char *ret = realloc(*cmt, (len + offset + 1) * sizeof(char));
+ if (ret != NULL) {
+ memcpy(ret + offset, &Bytes[1], len);
+ ret[len + offset] = 0;
+ *cmt = ret;
+ return GIF_OK;
+ }
+ return GIF_ERROR;
+}
+
+static void packARGB32(argb *pixel, GifByteType alpha, GifByteType red, GifByteType green, GifByteType blue) {
+ pixel->alpha = alpha;
+ pixel->red = red;
+ pixel->green = green;
+ pixel->blue = blue;
+}
+
+static void getColorFromTable(int idx, argb *dst, const ColorMapObject *cmap) {
+ int colIdx = (idx >= cmap->ColorCount) ? 0 : idx;
+ GifColorType *col = &cmap->Colors[colIdx];
+ packARGB32(dst, 0xFF, col->Red, col->Green, col->Blue);
+}
+
+static void eraseColor(argb *bm, int w, int h, argb color) {
+ int i;
+ for (i = 0; i < w * h; i++) {
+ *(bm + i) = color;
+ }
+}
+
+static inline bool setupBackupBmp(GifInfo *info, short transpIndex) {
+ GifFileType *fGIF = info->gifFilePtr;
+ info->backupPtr = calloc(fGIF->SWidth * fGIF->SHeight, sizeof(argb));
+ if (!info->backupPtr) {
+ info->gifFilePtr->Error = D_GIF_ERR_NOT_ENOUGH_MEM;
+ return false;
+ }
+ argb paintingColor;
+ if (transpIndex == -1) {
+ getColorFromTable(fGIF->SBackGroundColor, &paintingColor, fGIF->SColorMap);
+ } else {
+ packARGB32(&paintingColor, 0, 0, 0, 0);
+ }
+ eraseColor(info->backupPtr, fGIF->SWidth, fGIF->SHeight, paintingColor);
+ return true;
+}
+
+static int readExtensions(int ExtFunction, GifByteType *ExtData, GifInfo *info) {
+ if (ExtData == NULL) {
+ return GIF_OK;
+ }
+ if (ExtFunction == GRAPHICS_EXT_FUNC_CODE && ExtData[0] == 4) {
+ FrameInfo *fi = &info->infos[info->gifFilePtr->ImageCount];
+ fi->transpIndex = -1;
+ char *b = (char*) ExtData + 1;
+ short delay = ((b[2] << 8) | b[1]);
+ fi->duration = delay > 1 ? delay * 10 : 100;
+ fi->disposalMethod = ((b[0] >> 2) & 7);
+ if (ExtData[1] & 1) {
+ fi->transpIndex = 0xff & b[3];
+ }
+ if (fi->disposalMethod == 3 && info->backupPtr == NULL) {
+ if (!setupBackupBmp(info, fi->transpIndex)) {
+ return GIF_ERROR;
+ }
+ }
+ } else if (ExtFunction == COMMENT_EXT_FUNC_CODE) {
+ if (getComment(ExtData, &info->comment) == GIF_ERROR) {
+ info->gifFilePtr->Error = D_GIF_ERR_NOT_ENOUGH_MEM;
+ return GIF_ERROR;
+ }
+ } else if (ExtFunction == APPLICATION_EXT_FUNC_CODE && ExtData[0] == 11) {
+ if (strncmp("NETSCAPE2.0", &ExtData[1], 11) == 0 || strncmp("ANIMEXTS1.0", &ExtData[1], 11) == 0) {
+ if (DGifGetExtensionNext(info->gifFilePtr, &ExtData, &ExtFunction) == GIF_ERROR) {
+ return GIF_ERROR;
+ }
+ if (ExtFunction == APPLICATION_EXT_FUNC_CODE && ExtData[0] == 3 && ExtData[1] == 1) {
+ info->loopCount = (unsigned short) (ExtData[2] + (ExtData[3] << 8));
+ }
+ }
+ }
+ return GIF_OK;
+}
+
+static int DDGifSlurp(GifFileType *GifFile, GifInfo* info, bool shouldDecode) {
+ GifRecordType RecordType;
+ GifByteType *ExtData;
+ int codeSize;
+ int ExtFunction;
+ size_t ImageSize;
+ do {
+ if (DGifGetRecordType(GifFile, &RecordType) == GIF_ERROR) {
+ return (GIF_ERROR);
+ }
+ switch (RecordType) {
+ case IMAGE_DESC_RECORD_TYPE:
+
+ if (DGifGetImageDesc(GifFile, !shouldDecode) == GIF_ERROR) {
+ return (GIF_ERROR);
+ }
+ int i = shouldDecode ? info->currentIndex : GifFile->ImageCount - 1;
+ SavedImage *sp = &GifFile->SavedImages[i];
+ ImageSize = sp->ImageDesc.Width * sp->ImageDesc.Height;
+
+ if (sp->ImageDesc.Width < 1 || sp->ImageDesc.Height < 1 || ImageSize > (SIZE_MAX / sizeof(GifPixelType))) {
+ GifFile->Error = D_GIF_ERR_INVALID_IMG_DIMS;
+ return GIF_ERROR;
+ }
+ if (sp->ImageDesc.Width > GifFile->SWidth || sp->ImageDesc.Height > GifFile->SHeight) {
+ GifFile->Error = D_GIF_ERR_IMG_NOT_CONFINED;
+ return GIF_ERROR;
+ }
+ if (shouldDecode) {
+ sp->RasterBits = info->rasterBits;
+ if (sp->ImageDesc.Interlace) {
+ int i, j;
+ int InterlacedOffset[] = { 0, 4, 2, 1 };
+ int InterlacedJumps[] = { 8, 8, 4, 2 };
+ for (i = 0; i < 4; i++) {
+ for (j = InterlacedOffset[i]; j < sp->ImageDesc.Height; j += InterlacedJumps[i]) {
+ if (DGifGetLine(GifFile, sp->RasterBits + j * sp->ImageDesc.Width, sp->ImageDesc.Width) == GIF_ERROR) {
+ return GIF_ERROR;
+ }
+ }
+ }
+ } else {
+ if (DGifGetLine(GifFile, sp->RasterBits, ImageSize) == GIF_ERROR) {
+ return (GIF_ERROR);
+ }
+ }
+ if (info->currentIndex >= GifFile->ImageCount - 1) {
+ if (info->loopCount > 0) {
+ info->currentLoop++;
+ }
+ if (fileRewindFun(info) != 0) {
+ info->gifFilePtr->Error = D_GIF_ERR_READ_FAILED;
+ return GIF_ERROR;
+ }
+ }
+ return GIF_OK;
+ } else {
+ if (DGifGetCode(GifFile, &codeSize, &ExtData) == GIF_ERROR) {
+ return (GIF_ERROR);
+ }
+ while (ExtData != NULL) {
+ if (DGifGetCodeNext(GifFile, &ExtData) == GIF_ERROR) {
+ return (GIF_ERROR);
+ }
+ }
+ }
+ break;
+
+ case EXTENSION_RECORD_TYPE:
+ if (DGifGetExtension(GifFile, &ExtFunction, &ExtData) == GIF_ERROR) {
+ return (GIF_ERROR);
+ }
+
+ if (!shouldDecode) {
+ FrameInfo *tmpInfos = realloc(info->infos, (GifFile->ImageCount + 1) * sizeof(FrameInfo));
+ if (tmpInfos == NULL) {
+ return GIF_ERROR;
+ }
+ info->infos = tmpInfos;
+ if (readExtensions(ExtFunction, ExtData, info) == GIF_ERROR) {
+ return GIF_ERROR;
+ }
+ }
+ while (ExtData != NULL) {
+ if (DGifGetExtensionNext(GifFile, &ExtData, &ExtFunction) == GIF_ERROR) {
+ return (GIF_ERROR);
+ }
+ if (!shouldDecode) {
+ if (readExtensions(ExtFunction, ExtData, info) == GIF_ERROR) {
+ return GIF_ERROR;
+ }
+ }
+ }
+ break;
+
+ case TERMINATE_RECORD_TYPE:
+ break;
+
+ default:
+ break;
+ }
+ } while (RecordType != TERMINATE_RECORD_TYPE);
+ bool ok = true;
+ if (shouldDecode) {
+ ok = (fileRewindFun(info) == 0);
+ }
+ if (ok) {
+ return (GIF_OK);
+ } else {
+ info->gifFilePtr->Error = D_GIF_ERR_READ_FAILED;
+ return (GIF_ERROR);
+ }
+}
+
+static void copyLine(argb *dst, const unsigned char *src, const ColorMapObject *cmap, int transparent, int width) {
+ for (; width > 0; width--, src++, dst++) {
+ if (*src != transparent) {
+ getColorFromTable(*src, dst, cmap);
+ }
+ }
+}
+
+static argb *getAddr(argb *bm, int width, int left, int top) {
+ return bm + top * width + left;
+}
+
+static void blitNormal(argb *bm, int width, int height, const SavedImage *frame, const ColorMapObject *cmap, int transparent) {
+ const unsigned char* src = (unsigned char*) frame->RasterBits;
+ argb *dst = getAddr(bm, width, frame->ImageDesc.Left, frame->ImageDesc.Top);
+ GifWord copyWidth = frame->ImageDesc.Width;
+ if (frame->ImageDesc.Left + copyWidth > width) {
+ copyWidth = width - frame->ImageDesc.Left;
+ }
+
+ GifWord copyHeight = frame->ImageDesc.Height;
+ if (frame->ImageDesc.Top + copyHeight > height) {
+ copyHeight = height - frame->ImageDesc.Top;
+ }
+
+ for (; copyHeight > 0; copyHeight--) {
+ copyLine(dst, src, cmap, transparent, copyWidth);
+ src += frame->ImageDesc.Width;
+ dst += width;
+ }
+}
+
+static void fillRect(argb *bm, int bmWidth, int bmHeight, GifWord left, GifWord top, GifWord width, GifWord height, argb col) {
+ uint32_t* dst = (uint32_t*) getAddr(bm, bmWidth, left, top);
+ GifWord copyWidth = width;
+ if (left + copyWidth > bmWidth) {
+ copyWidth = bmWidth - left;
+ }
+
+ GifWord copyHeight = height;
+ if (top + copyHeight > bmHeight) {
+ copyHeight = bmHeight - top;
+ }
+ uint32_t* pColor = (uint32_t *) (&col);
+ for (; copyHeight > 0; copyHeight--) {
+ memset(dst, *pColor, copyWidth * sizeof(argb));
+ dst += bmWidth;
+ }
+}
+
+static void drawFrame(argb *bm, int bmWidth, int bmHeight, const SavedImage *frame, const ColorMapObject *cmap, short transpIndex) {
+ if (frame->ImageDesc.ColorMap != NULL) {
+ cmap = frame->ImageDesc.ColorMap;
+ if (cmap->ColorCount != (1 << cmap->BitsPerPixel)) {
+ cmap = defaultCmap;
+ }
+ }
+ blitNormal(bm, bmWidth, bmHeight, frame, cmap, transpIndex);
+}
+
+static bool checkIfCover(const SavedImage *target, const SavedImage *covered) {
+ if (target->ImageDesc.Left <= covered->ImageDesc.Left
+ && covered->ImageDesc.Left + covered->ImageDesc.Width
+ <= target->ImageDesc.Left + target->ImageDesc.Width
+ && target->ImageDesc.Top <= covered->ImageDesc.Top
+ && covered->ImageDesc.Top + covered->ImageDesc.Height
+ <= target->ImageDesc.Top + target->ImageDesc.Height) {
+ return true;
+ }
+ return false;
+}
+
+static inline void disposeFrameIfNeeded(argb *bm, GifInfo *info, unsigned int idx) {
+ argb* backup = info->backupPtr;
+ argb color;
+ packARGB32(&color, 0, 0, 0, 0);
+ GifFileType *fGif = info->gifFilePtr;
+ SavedImage* cur = &fGif->SavedImages[idx - 1];
+ SavedImage* next = &fGif->SavedImages[idx];
+ bool curTrans = info->infos[idx - 1].transpIndex != -1;
+ int curDisposal = info->infos[idx - 1].disposalMethod;
+ bool nextTrans = info->infos[idx].transpIndex != -1;
+ int nextDisposal = info->infos[idx].disposalMethod;
+ argb *tmp;
+ if ((curDisposal == 2 || curDisposal == 3) && (nextTrans || !checkIfCover(next, cur))) {
+ switch (curDisposal) {
+ case 2:
+
+ fillRect(bm, fGif->SWidth, fGif->SHeight, cur->ImageDesc.Left, cur->ImageDesc.Top, cur->ImageDesc.Width, cur->ImageDesc.Height, color);
+ break;
+
+ case 3:
+ tmp = bm;
+ bm = backup;
+ backup = tmp;
+ break;
+ }
+ }
+
+ if (nextDisposal == 3) {
+ memcpy(backup, bm, fGif->SWidth * fGif->SHeight * sizeof(argb));
+ }
+}
+
+static void reset(GifInfo *info) {
+ if (fileRewindFun(info) != 0) {
+ return;
+ }
+ info->nextStartTime = 0;
+ info->currentLoop = -1;
+ info->currentIndex = -1;
+}
+
+static void getBitmap(argb *bm, GifInfo *info) {
+ GifFileType* fGIF = info->gifFilePtr;
+
+ argb paintingColor;
+ int i = info->currentIndex;
+ if (DDGifSlurp(fGIF, info, true) == GIF_ERROR) {
+ return;
+ }
+ SavedImage* cur = &fGIF->SavedImages[i];
+ int transpIndex = info->infos[i].transpIndex;
+ if (i == 0) {
+ if (transpIndex == -1) {
+ getColorFromTable(fGIF->SBackGroundColor, &paintingColor, fGIF->SColorMap);
+ } else {
+ packARGB32(&paintingColor, 0, 0, 0, 0);
+ }
+ eraseColor(bm, fGIF->SWidth, fGIF->SHeight, paintingColor);
+ } else {
+ disposeFrameIfNeeded(bm, info, i);
+ }
+ drawFrame(bm, fGIF->SWidth, fGIF->SHeight, cur, fGIF->SColorMap, transpIndex);
+}
+
+static void setMetaData(int width, int height, int ImageCount, int errorCode, JNIEnv *env, jintArray metaData) {
+ jint *const ints = (*env)->GetIntArrayElements(env, metaData, 0);
+ if (ints == NULL) {
+ return;
+ }
+ ints[0] = width;
+ ints[1] = height;
+ ints[2] = ImageCount;
+ ints[3] = errorCode;
+ (*env)->ReleaseIntArrayElements(env, metaData, ints, 0);
+}
+
+static jint open(GifFileType *GifFileIn, int Error, int startPos, JNIEnv *env, jintArray metaData) {
+ if (startPos < 0) {
+ Error = D_GIF_ERR_NOT_READABLE;
+ DGifCloseFile(GifFileIn);
+ }
+ if (Error != 0 || GifFileIn == NULL) {
+ setMetaData(0, 0, 0, Error, env, metaData);
+ return (jint) NULL;
+ }
+ int width = GifFileIn->SWidth, height = GifFileIn->SHeight;
+ unsigned int wxh = width * height;
+ if (wxh < 1 || wxh > INT_MAX) {
+ DGifCloseFile(GifFileIn);
+ setMetaData(width, height, 0, D_GIF_ERR_INVALID_SCR_DIMS, env, metaData);
+ return (jint) NULL;
+ }
+ GifInfo *info = malloc(sizeof(GifInfo));
+ if (info == NULL) {
+ DGifCloseFile(GifFileIn);
+ setMetaData(width, height, 0, D_GIF_ERR_NOT_ENOUGH_MEM, env, metaData);
+ return (jint) NULL;
+ }
+ info->gifFilePtr = GifFileIn;
+ info->startPos = startPos;
+ info->currentIndex = -1;
+ info->nextStartTime = 0;
+ info->lastFrameReaminder = ULONG_MAX;
+ info->comment = NULL;
+ info->loopCount = 0;
+ info->currentLoop = -1;
+ info->speedFactor = 1.0;
+ info->rasterBits = calloc(GifFileIn->SHeight * GifFileIn->SWidth, sizeof(GifPixelType));
+ info->infos = malloc(sizeof(FrameInfo));
+ info->backupPtr = NULL;
+
+ if (info->rasterBits == NULL || info->infos == NULL) {
+ cleanUp(info);
+ setMetaData(width, height, 0, D_GIF_ERR_NOT_ENOUGH_MEM, env, metaData);
+ return (jint) NULL;
+ }
+ info->infos->duration = 0;
+ info->infos->disposalMethod = 0;
+ info->infos->transpIndex = -1;
+ if (GifFileIn->SColorMap == NULL || GifFileIn->SColorMap->ColorCount != (1 << GifFileIn->SColorMap->BitsPerPixel)) {
+ GifFreeMapObject(GifFileIn->SColorMap);
+ GifFileIn->SColorMap = defaultCmap;
+ }
+
+ DDGifSlurp(GifFileIn, info, false);
+
+ int imgCount = GifFileIn->ImageCount;
+
+ if (imgCount < 1) {
+ Error = D_GIF_ERR_NO_FRAMES;
+ }
+ if (fileRewindFun(info) != 0) {
+ Error = D_GIF_ERR_READ_FAILED;
+ }
+ if (Error != 0) {
+ cleanUp(info);
+ }
+ setMetaData(width, height, imgCount, Error, env, metaData);
+ return (jint)(Error == 0 ? info : NULL);
+}
+
+JNIEXPORT jlong JNICALL Java_org_telegram_ui_Components_GifDrawable_getAllocationByteCount(JNIEnv *env, jclass class, jobject gifInfo) {
+ GifInfo *info = (GifInfo *)gifInfo;
+ if (info == NULL) {
+ return 0;
+ }
+ unsigned int pxCount = info->gifFilePtr->SWidth + info->gifFilePtr->SHeight;
+ jlong sum = pxCount * sizeof(char);
+ if (info->backupPtr != NULL) {
+ sum += pxCount * sizeof(argb);
+ }
+ return sum;
+}
+
+JNIEXPORT void JNICALL Java_org_telegram_ui_Components_GifDrawable_reset(JNIEnv *env, jclass class, jobject gifInfo) {
+ GifInfo *info = (GifInfo *)gifInfo;
+ if (info == NULL) {
+ return;
+ }
+ reset(info);
+}
+
+JNIEXPORT void JNICALL Java_org_telegram_ui_Components_GifDrawable_setSpeedFactor(JNIEnv *env, jclass class, jobject gifInfo, jfloat factor) {
+ GifInfo *info = (GifInfo *)gifInfo;
+ if (info == NULL) {
+ return;
+ }
+ info->speedFactor = factor;
+}
+
+JNIEXPORT void JNICALL Java_org_telegram_ui_Components_GifDrawable_seekToTime(JNIEnv *env, jclass class, jobject gifInfo, jint desiredPos, jintArray jPixels) {
+ GifInfo *info = (GifInfo *)gifInfo;
+ if (info == NULL || jPixels == NULL) {
+ return;
+ }
+ int imgCount = info->gifFilePtr->ImageCount;
+ if (imgCount <= 1) {
+ return;
+ }
+
+ unsigned long sum = 0;
+ int i;
+ for (i = 0; i < imgCount; i++) {
+ unsigned long newSum = sum + info->infos[i].duration;
+ if (newSum >= desiredPos) {
+ break;
+ }
+ sum = newSum;
+ }
+ if (i < info->currentIndex) {
+ return;
+ }
+
+ unsigned long lastFrameRemainder = desiredPos - sum;
+ if (i == imgCount - 1 && lastFrameRemainder > info->infos[i].duration) {
+ lastFrameRemainder = info->infos[i].duration;
+ }
+ if (i > info->currentIndex) {
+ jint *const pixels = (*env)->GetIntArrayElements(env, jPixels, 0);
+ if (pixels == NULL) {
+ return;
+ }
+ while (info->currentIndex <= i) {
+ info->currentIndex++;
+ getBitmap((argb*) pixels, info);
+ }
+ (*env)->ReleaseIntArrayElements(env, jPixels, pixels, 0);
+ }
+ info->lastFrameReaminder = lastFrameRemainder;
+
+ if (info->speedFactor == 1.0) {
+ info->nextStartTime = getRealTime() + lastFrameRemainder;
+ } else {
+ info->nextStartTime = getRealTime() + lastFrameRemainder * info->speedFactor;
+ }
+}
+
+JNIEXPORT void JNICALL Java_org_telegram_ui_Components_GifDrawable_seekToFrame(JNIEnv *env, jclass class, jobject gifInfo, jint desiredIdx, jintArray jPixels) {
+ GifInfo *info = (GifInfo *)gifInfo;
+ if (info == NULL|| jPixels==NULL) {
+ return;
+ }
+ if (desiredIdx <= info->currentIndex) {
+ return;
+ }
+
+ int imgCount = info->gifFilePtr->ImageCount;
+ if (imgCount <= 1) {
+ return;
+ }
+
+ jint *const pixels = (*env)->GetIntArrayElements(env, jPixels, 0);
+ if (pixels == NULL) {
+ return;
+ }
+
+ info->lastFrameReaminder = 0;
+ if (desiredIdx >= imgCount) {
+ desiredIdx = imgCount - 1;
+ }
+
+ while (info->currentIndex < desiredIdx) {
+ info->currentIndex++;
+ getBitmap((argb *) pixels, info);
+ }
+ (*env)->ReleaseIntArrayElements(env, jPixels, pixels, 0);
+ if (info->speedFactor == 1.0) {
+ info->nextStartTime = getRealTime() + info->infos[info->currentIndex].duration;
+ } else {
+ info->nextStartTime = getRealTime() + info->infos[info->currentIndex].duration * info->speedFactor;
+ }
+}
+
+JNIEXPORT void JNICALL Java_org_telegram_ui_Components_GifDrawable_renderFrame(JNIEnv *env, jclass class, jintArray jPixels, jobject gifInfo, jintArray metaData) {
+ GifInfo *info = (GifInfo *)gifInfo;
+ if (info == NULL || jPixels == NULL) {
+ return;
+ }
+ bool needRedraw = false;
+ unsigned long rt = getRealTime();
+
+ if (rt >= info->nextStartTime && info->currentLoop < info->loopCount) {
+ if (++info->currentIndex >= info->gifFilePtr->ImageCount) {
+ info->currentIndex = 0;
+ }
+ needRedraw = true;
+ }
+ jint *const rawMetaData = (*env)->GetIntArrayElements(env, metaData, 0);
+ if (rawMetaData == NULL) {
+ return;
+ }
+
+ if (needRedraw) {
+ jint *const pixels = (*env)->GetIntArrayElements(env, jPixels, 0);
+ if (pixels == NULL) {
+ (*env)->ReleaseIntArrayElements(env, metaData, rawMetaData, 0);
+ return;
+ }
+ getBitmap((argb *)pixels, info);
+ rawMetaData[3] = info->gifFilePtr->Error;
+
+ (*env)->ReleaseIntArrayElements(env, jPixels, pixels, 0);
+ unsigned int scaledDuration = info->infos[info->currentIndex].duration;
+ if (info->speedFactor != 1.0) {
+ scaledDuration /= info->speedFactor;
+ if (scaledDuration<=0) {
+ scaledDuration=1;
+ } else if (scaledDuration > INT_MAX) {
+ scaledDuration = INT_MAX;
+ }
+ }
+ info->nextStartTime = rt + scaledDuration;
+ rawMetaData[4] = scaledDuration;
+ } else {
+ long delay = info->nextStartTime-rt;
+ if (delay < 0) {
+ rawMetaData[4] = -1;
+ } else {
+ rawMetaData[4] = (int) delay;
+ }
+ }
+ (*env)->ReleaseIntArrayElements(env, metaData, rawMetaData, 0);
+}
+
+JNIEXPORT void JNICALL Java_org_telegram_ui_Components_GifDrawable_free(JNIEnv *env, jclass class, jobject gifInfo) {
+ if (gifInfo == NULL) {
+ return;
+ }
+ GifInfo *info = (GifInfo *)gifInfo;
+ FILE *file = info->gifFilePtr->UserData;
+ if (file) {
+ fclose(file);
+ }
+ info->gifFilePtr->UserData = NULL;
+ cleanUp(info);
+}
+
+JNIEXPORT jstring JNICALL Java_org_telegram_ui_Components_GifDrawable_getComment(JNIEnv *env, jclass class, jobject gifInfo) {
+ if (gifInfo == NULL) {
+ return NULL;
+ }
+ GifInfo *info = (GifInfo *)gifInfo;
+ return (*env)->NewStringUTF(env, info->comment);
+}
+
+JNIEXPORT jint JNICALL Java_org_telegram_ui_Components_GifDrawable_getLoopCount(JNIEnv *env, jclass class, jobject gifInfo) {
+ if (gifInfo == NULL) {
+ return 0;
+ }
+ return ((GifInfo *)gifInfo)->loopCount;
+}
+
+JNIEXPORT jint JNICALL Java_org_telegram_ui_Components_GifDrawable_getDuration(JNIEnv *env, jclass class, jobject gifInfo) {
+ GifInfo *info = (GifInfo *)gifInfo;
+ if (info == NULL) {
+ return 0;
+ }
+ int i;
+ unsigned long sum = 0;
+ for (i = 0; i < info->gifFilePtr->ImageCount; i++) {
+ sum += info->infos[i].duration;
+ }
+ return sum;
+}
+
+JNIEXPORT jint JNICALL Java_org_telegram_ui_Components_GifDrawable_getCurrentPosition(JNIEnv *env, jclass class, jobject gifInfo) {
+ GifInfo *info = (GifInfo *)gifInfo;
+ if (info == NULL) {
+ return 0;
+ }
+ int idx = info->currentIndex;
+ if (idx < 0 || info->gifFilePtr->ImageCount <= 1) {
+ return 0;
+ }
+ int i;
+ unsigned int sum = 0;
+ for (i = 0; i < idx; i++) {
+ sum += info->infos[i].duration;
+ }
+ unsigned long remainder = info->lastFrameReaminder == ULONG_MAX ? getRealTime() - info->nextStartTime : info->lastFrameReaminder;
+ return (int) (sum + remainder);
+}
+
+JNIEXPORT void JNICALL Java_org_telegram_ui_Components_GifDrawable_saveRemainder(JNIEnv *env, jclass class, jobject gifInfo) {
+ GifInfo *info = (GifInfo *)gifInfo;
+ if (info == NULL) {
+ return;
+ }
+ info->lastFrameReaminder = getRealTime() - info->nextStartTime;
+}
+
+JNIEXPORT void JNICALL Java_org_telegram_ui_Components_GifDrawable_restoreRemainder(JNIEnv *env, jclass class, jobject gifInfo) {
+ GifInfo *info = (GifInfo *)gifInfo;
+ if (info == NULL || info->lastFrameReaminder == ULONG_MAX) {
+ return;
+ }
+ info->nextStartTime = getRealTime() + info->lastFrameReaminder;
+ info->lastFrameReaminder = ULONG_MAX;
+}
+
+JNIEXPORT jint JNICALL Java_org_telegram_ui_Components_GifDrawable_openFile(JNIEnv *env, jclass class, jintArray metaData, jstring jfname) {
+ if (jfname == NULL) {
+ setMetaData(0, 0, 0, D_GIF_ERR_OPEN_FAILED, env, metaData);
+ return (jint) NULL;
+ }
+
+ const char *const fname = (*env)->GetStringUTFChars(env, jfname, 0);
+ FILE *file = fopen(fname, "rb");
+ (*env)->ReleaseStringUTFChars(env, jfname, fname);
+ if (file == NULL) {
+ setMetaData(0, 0, 0, D_GIF_ERR_OPEN_FAILED, env, metaData);
+ return (jint) NULL;
+ }
+ int Error = 0;
+ GifFileType *GifFileIn = DGifOpen(file, &fileReadFunc, &Error);
+ return open(GifFileIn, Error, ftell(file), env, metaData);
+}