diff --git a/src/main/jni/bitmap.c b/src/main/jni/bitmap.c index 92d5c79a..b47b907f 100644 --- a/src/main/jni/bitmap.c +++ b/src/main/jni/bitmap.c @@ -64,7 +64,7 @@ Java_pl_droidsonroids_gif_GifInfoHandle_renderFrame(JNIEnv *env, jclass __unused if (lockPixels(env, jbitmap, info, &pixels) != 0) { return 0; } - DDGifSlurp(info, true); + DDGifSlurp(info, true, false); if (info->currentIndex == 0) prepareCanvas(pixels, info); const uint_fast32_t frameDuration = getBitmap((argb *) pixels, info); diff --git a/src/main/jni/control.c b/src/main/jni/control.c index f239fc64..5386ed1e 100644 --- a/src/main/jni/control.c +++ b/src/main/jni/control.c @@ -36,23 +36,46 @@ static uint_fast32_t seekBitmap(GifInfo *info, JNIEnv *env, jint desiredIndex, j if (lockPixels(env, jbitmap, info, &pixels) != 0) { return 0; } - uint_fast32_t duration = seek(info, desiredIndex, pixels); + uint_fast32_t duration = seek(info, (uint_fast32_t) desiredIndex, pixels); unlockPixels(env, jbitmap); return duration; } -uint_fast32_t seek(GifInfo *info, jint desiredIndex, const void *pixels) { - if (desiredIndex < info->currentIndex && !reset(info)) { - info->gifFilePtr->Error = D_GIF_ERR_REWIND_FAILED; - return 0; - } - if (desiredIndex >= info->gifFilePtr->ImageCount) { - desiredIndex = (jint) (info->gifFilePtr->ImageCount - 1); - } - if (info->currentIndex == 0) +uint_fast32_t seek(GifInfo *info, uint_fast32_t desiredIndex, const void *pixels) { + GifFileType *const gifFilePtr = info->gifFilePtr; + if (desiredIndex < info->currentIndex || info->currentIndex == 0) { + if (!reset(info)) { + gifFilePtr->Error = D_GIF_ERR_REWIND_FAILED; + return 0; + } prepareCanvas(pixels, info); + } + if (desiredIndex >= gifFilePtr->ImageCount) { + desiredIndex = gifFilePtr->ImageCount - 1; + } + + uint_fast32_t i; + for (i = desiredIndex; i > info->currentIndex; i--) { + const GifImageDesc imageDesc = info->gifFilePtr->SavedImages[i].ImageDesc; + if (gifFilePtr->SWidth == imageDesc.Width && gifFilePtr->SHeight == imageDesc.Height) { + const GraphicsControlBlock controlBlock = info->controlBlock[i]; + if (controlBlock.TransparentColor == NO_TRANSPARENT_COLOR) { + break; + } else if (controlBlock.DisposalMode == DISPOSE_BACKGROUND) { + break; + } + } + } + + if (i > 0) { + while (info->currentIndex < i - 1) { + DDGifSlurp(info, false, true); + ++info->currentIndex; + } + } + do { - DDGifSlurp(info, true); + DDGifSlurp(info, true, false); drawNextBitmap((argb *) pixels, info); } while (info->currentIndex++ < desiredIndex); --info->currentIndex; @@ -122,7 +145,7 @@ Java_pl_droidsonroids_gif_GifInfoHandle_restoreRemainder(JNIEnv *__unused env, (info->loopCount > 0 && info->currentLoop == info->loopCount)) return -1; info->nextStartTime = getRealTime() + info->lastFrameRemainder; - const long remainder = info->lastFrameRemainder; + const long long remainder = info->lastFrameRemainder; info->lastFrameRemainder = -1; return remainder; } diff --git a/src/main/jni/decoding.c b/src/main/jni/decoding.c index 6b6425ed..3317654d 100644 --- a/src/main/jni/decoding.c +++ b/src/main/jni/decoding.c @@ -1,21 +1,23 @@ #include "gif.h" -void DDGifSlurp(GifInfo *info, bool shouldDecode) { +void DDGifSlurp(GifInfo *info, bool decode, bool exitAfterFrame) { GifRecordType RecordType; GifByteType *ExtData; int ExtFunction; GifFileType *gifFilePtr; gifFilePtr = info->gifFilePtr; + uint_fast32_t lastAllocatedGCB = 0; do { if (DGifGetRecordType(gifFilePtr, &RecordType) == GIF_ERROR) return; + bool isInitialPass = !decode && !exitAfterFrame; switch (RecordType) { case IMAGE_DESC_RECORD_TYPE: - if (DGifGetImageDesc(gifFilePtr, !shouldDecode) == GIF_ERROR) + if (DGifGetImageDesc(gifFilePtr, isInitialPass) == GIF_ERROR) return; - if (!shouldDecode) { + if (isInitialPass) { SavedImage *sp = &gifFilePtr->SavedImages[gifFilePtr->ImageCount - 1]; int_fast32_t topOverflow = gifFilePtr->Image.Top + gifFilePtr->Image.Height - gifFilePtr->SHeight; @@ -35,7 +37,7 @@ void DDGifSlurp(GifInfo *info, bool shouldDecode) { } } - if (shouldDecode) { + if (decode) { int_fast32_t widthOverflow = gifFilePtr->Image.Width - info->originalWidth; int_fast32_t heightOverflow = gifFilePtr->Image.Height - info->originalHeight; if (widthOverflow > 0 || heightOverflow > 0) { @@ -60,8 +62,7 @@ void DDGifSlurp(GifInfo *info, bool shouldDecode) { if (DGifGetLine(gifFilePtr, info->rasterBits + j * gifFilePtr->Image.Width, gifFilePtr->Image.Width) == GIF_ERROR) return; } - } - else { + } else { if (DGifGetLine(gifFilePtr, info->rasterBits, gifFilePtr->Image.Width * gifFilePtr->Image.Height) == GIF_ERROR) { return; } @@ -79,43 +80,45 @@ void DDGifSlurp(GifInfo *info, bool shouldDecode) { *dst = *src; dst++; src += info->sampleSize; - } - while (src < srcEndLine); + } while (src < srcEndLine); dst = dstEndLine; src = srcNextLineStart; - } - while (src < srcEndImage); + } while (src < srcEndImage); } return; - } - else { + } else { do { if (DGifGetCodeNext(gifFilePtr, &ExtData) == GIF_ERROR) { return; } + } while (ExtData != NULL); + if (exitAfterFrame) { + return; } - while (ExtData != NULL); } break; case EXTENSION_RECORD_TYPE: if (DGifGetExtension(gifFilePtr, &ExtFunction, &ExtData) == GIF_ERROR) return; - if (!shouldDecode) { - GraphicsControlBlock *tmpInfos = reallocarray(info->controlBlock, info->gifFilePtr->ImageCount + 1, sizeof(GraphicsControlBlock)); - if (tmpInfos == NULL) { - gifFilePtr->Error = D_GIF_ERR_NOT_ENOUGH_MEM; - return; + if (isInitialPass) { + if (lastAllocatedGCB < info->gifFilePtr->ImageCount) { + GraphicsControlBlock *tmpInfos = reallocarray(info->controlBlock, info->gifFilePtr->ImageCount + 1, sizeof(GraphicsControlBlock)); + if (tmpInfos == NULL) { + gifFilePtr->Error = D_GIF_ERR_NOT_ENOUGH_MEM; + return; + } + lastAllocatedGCB = info->gifFilePtr->ImageCount; + info->controlBlock = tmpInfos; + info->controlBlock[gifFilePtr->ImageCount].DelayTime = DEFAULT_FRAME_DURATION_MS; } - info->controlBlock = tmpInfos; - info->controlBlock[gifFilePtr->ImageCount].DelayTime = DEFAULT_FRAME_DURATION_MS; if (readExtensions(ExtFunction, ExtData, info) == GIF_ERROR) return; } while (ExtData != NULL) { if (DGifGetExtensionNext(info->gifFilePtr, &ExtData) == GIF_ERROR) return; - if (!shouldDecode) { + if (isInitialPass) { if (readExtensions(ExtFunction, ExtData, info) == GIF_ERROR) return; } diff --git a/src/main/jni/drawing.c b/src/main/jni/drawing.c index 7e656c06..bc719ab1 100644 --- a/src/main/jni/drawing.c +++ b/src/main/jni/drawing.c @@ -90,7 +90,7 @@ static inline void disposeFrameIfNeeded(argb *bm, GifInfo *info) { // and completely covers current area uint_fast8_t curDisposal = info->controlBlock[info->currentIndex - 1].DisposalMode; bool nextTrans = info->controlBlock[info->currentIndex].TransparentColor != NO_TRANSPARENT_COLOR; - unsigned char nextDisposal = info->controlBlock[info->currentIndex].DisposalMode; + uint_fast8_t nextDisposal = info->controlBlock[info->currentIndex].DisposalMode; if ((curDisposal == DISPOSE_PREVIOUS || nextDisposal == DISPOSE_PREVIOUS) && info->backupPtr == NULL) { info->backupPtr = calloc(info->stride * fGif->SHeight, sizeof(argb)); @@ -121,7 +121,7 @@ static inline void disposeFrameIfNeeded(argb *bm, GifInfo *info) { memcpy(backup, bm, info->stride * fGif->SHeight * sizeof(argb)); } -void prepareCanvas(argb *bm, GifInfo *info) { +void prepareCanvas(const argb *bm, GifInfo *info) { if (info->gifFilePtr->SColorMap && info->controlBlock->TransparentColor == NO_TRANSPARENT_COLOR) { argb bgColArgb; bgColArgb.rgb = info->gifFilePtr->SColorMap->Colors[info->gifFilePtr->SBackGroundColor]; diff --git a/src/main/jni/gif.h b/src/main/jni/gif.h index 506af82c..3f80e73f 100644 --- a/src/main/jni/gif.h +++ b/src/main/jni/gif.h @@ -91,12 +91,12 @@ struct GifInfo { GifFileType *gifFilePtr; GifWord originalWidth, originalHeight; uint_fast16_t sampleSize; - long lastFrameRemainder; - long nextStartTime; + long long lastFrameRemainder; + long long nextStartTime; uint_fast32_t currentIndex; GraphicsControlBlock *controlBlock; argb *backupPtr; - long startPos; + long long startPos; unsigned char *rasterBits; char *comment; uint_fast16_t loopCount; @@ -132,7 +132,7 @@ typedef struct { typedef struct { GifFileType *GifFileIn; int Error; - long startPos; + long long startPos; RewindFunc rewindFunc; jlong sourceLength; } GifSourceDescriptor; @@ -175,7 +175,7 @@ static int getComment(GifByteType *Bytes, GifInfo *); static int readExtensions(int ExtFunction, GifByteType *ExtData, GifInfo *info); -__attribute__ ((visibility ("default"))) void DDGifSlurp(GifInfo *info, bool shouldDecode); +__attribute__ ((visibility ("default"))) void DDGifSlurp(GifInfo *info, bool decode, bool exitAfterFrame); void throwGifIOException(int errorCode, JNIEnv *env); @@ -197,11 +197,11 @@ int lockPixels(JNIEnv *env, jobject jbitmap, GifInfo *info, void **pixels); void unlockPixels(JNIEnv *env, jobject jbitmap); -__attribute__ ((visibility ("default"))) long calculateInvalidationDelay(GifInfo *info, long renderStartTime, uint_fast32_t frameDuration); +__attribute__ ((visibility ("default"))) long long calculateInvalidationDelay(GifInfo *info, long renderStartTime, uint_fast32_t frameDuration); __attribute__ ((visibility ("default"))) jint restoreSavedState(GifInfo *info, JNIEnv *env, jlongArray state, void *pixels); -__attribute__ ((visibility ("default"))) void prepareCanvas(argb *bm, GifInfo *info); +__attribute__ ((visibility ("default"))) void prepareCanvas(const argb *bm, GifInfo *info); __attribute__ ((visibility ("default"))) void drawNextBitmap(argb *bm, GifInfo *info); @@ -209,6 +209,6 @@ uint_fast32_t getFrameDuration(GifInfo *info); __attribute__ ((visibility ("default"))) JNIEnv *getEnv(); -__attribute__ ((visibility ("default"))) uint_fast32_t seek(GifInfo *info, jint desiredIndex, const void *pixels); +__attribute__ ((visibility ("default"))) uint_fast32_t seek(GifInfo *info, uint_fast32_t desiredIndex, const void *pixels); #endif diff --git a/src/main/jni/metadata.c b/src/main/jni/metadata.c index 3f3d8067..e72d6b8f 100644 --- a/src/main/jni/metadata.c +++ b/src/main/jni/metadata.c @@ -74,7 +74,7 @@ Java_pl_droidsonroids_gif_GifInfoHandle_getCurrentPosition(JNIEnv *__unused env, sum += info->controlBlock[i].DelayTime; } - long remainder; + long long remainder; if (info->lastFrameRemainder == -1) { remainder = info->nextStartTime - getRealTime(); if (remainder < 0) { //in case of if frame hasn't been rendered until nextStartTime passed @@ -167,7 +167,7 @@ jint restoreSavedState(GifInfo *info, JNIEnv *env, jlongArray state, void *pixel if (info->currentIndex == 0) prepareCanvas(pixels, info); while (info->currentIndex < savedIndex) { - DDGifSlurp(info, true); + DDGifSlurp(info, true, false); lastFrameDuration = getBitmap((argb *) pixels, info); } } diff --git a/src/main/jni/open_close.c b/src/main/jni/open_close.c index b87cc6b9..7ce8f2d1 100644 --- a/src/main/jni/open_close.c +++ b/src/main/jni/open_close.c @@ -55,7 +55,7 @@ GifInfo *createGifHandle(GifSourceDescriptor *descriptor, JNIEnv *env, jboolean info->isOpaque = JNI_FALSE; info->sampleSize = 1; - DDGifSlurp(info, false); + DDGifSlurp(info, false, false); if (justDecodeMetaData == JNI_TRUE) { info->rasterBits = NULL; } else { diff --git a/src/main/jni/opengl.c b/src/main/jni/opengl.c index 2bba5d5e..5b17ee64 100644 --- a/src/main/jni/opengl.c +++ b/src/main/jni/opengl.c @@ -25,13 +25,13 @@ static void *slurp(void *pVoidInfo) { GifInfo *info = pVoidInfo; while (1) { long renderStartTime = getRealTime(); - DDGifSlurp(info, true); + DDGifSlurp(info, true, false); TexImageDescriptor *texImageDescriptor = info->frameBufferDescriptor; if (info->currentIndex == 0) prepareCanvas(texImageDescriptor->frameBuffer, info); const uint_fast32_t frameDuration = getBitmap((argb *) texImageDescriptor->frameBuffer, info); - const long invalidationDelayMillis = calculateInvalidationDelay(info, renderStartTime, frameDuration); + const long long invalidationDelayMillis = calculateInvalidationDelay(info, renderStartTime, frameDuration); int pollResult = poll(&texImageDescriptor->eventPollFd, 1, (int) invalidationDelayMillis); eventfd_t eventValue; if (pollResult < 0) { @@ -120,8 +120,7 @@ Java_pl_droidsonroids_gif_GifInfoHandle_stopDecoderThread(JNIEnv *env, jclass __ } __unused JNIEXPORT void JNICALL -Java_pl_droidsonroids_gif_GifInfoHandle_renderGLFrame(JNIEnv *env, jclass __unused handleClass, jlong gifInfo, - jint desiredIndex) { +Java_pl_droidsonroids_gif_GifInfoHandle_renderGLFrame(JNIEnv *env, jclass __unused handleClass, jlong gifInfo, jint desiredIndex) { GifInfo *info = (GifInfo *) gifInfo; if (info == NULL) { return; @@ -144,7 +143,7 @@ Java_pl_droidsonroids_gif_GifInfoHandle_renderGLFrame(JNIEnv *env, jclass __unus texImageDescriptor->eventPollFd.fd = -1; } - seek(info, desiredIndex, texImageDescriptor->frameBuffer); + seek(info, (uint_fast32_t) desiredIndex, texImageDescriptor->frameBuffer); const GLsizei width = (const GLsizei) info->gifFilePtr->SWidth; const GLsizei height = (const GLsizei) info->gifFilePtr->SHeight; diff --git a/src/main/jni/surface.c b/src/main/jni/surface.c index be39cf8c..7a4c496d 100644 --- a/src/main/jni/surface.c +++ b/src/main/jni/surface.c @@ -27,7 +27,7 @@ static void *slurp(void *pVoidInfo) { } surfaceDescriptor->slurpHelper = 0; pthread_mutex_unlock(&surfaceDescriptor->slurpMutex); - DDGifSlurp(info, true); + DDGifSlurp(info, true, false); pthread_mutex_lock(&surfaceDescriptor->renderMutex); surfaceDescriptor->renderHelper = 1; pthread_cond_signal(&surfaceDescriptor->renderCond); @@ -128,7 +128,7 @@ Java_pl_droidsonroids_gif_GifInfoHandle_bindSurface(JNIEnv *env, jclass __unused const size_t bufferSize = buffer.stride * buffer.height * sizeof(argb); info->stride = buffer.stride; - long invalidationDelayMillis; + long long invalidationDelayMillis; if (surfaceDescriptor->surfaceBackupPtr) { memcpy(buffer.bits, surfaceDescriptor->surfaceBackupPtr, bufferSize); invalidationDelayMillis = 0; diff --git a/src/main/jni/time.c b/src/main/jni/time.c index 84bd706f..de5b9d83 100644 --- a/src/main/jni/time.c +++ b/src/main/jni/time.c @@ -1,8 +1,8 @@ #include "gif.h" -long calculateInvalidationDelay(GifInfo *info, long renderStartTime, uint_fast32_t frameDuration) { +long long calculateInvalidationDelay(GifInfo *info, long renderStartTime, uint_fast32_t frameDuration) { if (frameDuration) { - long invalidationDelay = frameDuration; + long long invalidationDelay = frameDuration; if (info->speedFactor != 1.0) { invalidationDelay /= info->speedFactor; }