void releaseSurfaceDescriptor(SurfaceDescriptor *surfaceDescriptor, JNIEnv *env) { if (surfaceDescriptor == NULL) return; free(surfaceDescriptor->surfaceBackupPtr); surfaceDescriptor->surfaceBackupPtr = NULL; THROW_ON_NONZERO_RESULT(close(surfaceDescriptor->eventPollFd.fd), "eventfd close failed"); THROW_ON_NONZERO_RESULT(pthread_mutex_destroy(&surfaceDescriptor->slurpMutex), "slurp mutex destroy failed"); THROW_ON_NONZERO_RESULT(pthread_mutex_destroy(&surfaceDescriptor->renderMutex), "render mutex destroy failed"); THROW_ON_NONZERO_RESULT(pthread_cond_destroy(&surfaceDescriptor->slurpCond), "slurp cond destroy failed"); THROW_ON_NONZERO_RESULT(pthread_cond_destroy(&surfaceDescriptor->renderCond), "render cond destroy failed"); }
static void releaseSurfaceDescriptor(GifInfo *info, JNIEnv *env) { SurfaceDescriptor *descriptor = info->frameBufferDescriptor; info->frameBufferDescriptor = NULL; free(descriptor->frameBuffer); if (close(descriptor->eventPollFd.fd) != 0 && errno != EINTR) { throwException(env, RUNTIME_EXCEPTION_ERRNO, "Eventfd close failed "); } errno = pthread_mutex_destroy(&descriptor->slurpMutex); THROW_ON_NONZERO_RESULT(errno, "Slurp mutex destroy failed "); errno = pthread_mutex_destroy(&descriptor->renderMutex); THROW_ON_NONZERO_RESULT(errno, "Render mutex destroy failed "); errno = pthread_cond_destroy(&descriptor->slurpCond); THROW_ON_NONZERO_RESULT(errno, "Slurp cond destroy failed "); errno = pthread_cond_destroy(&descriptor->renderCond); THROW_ON_NONZERO_RESULT(errno, "Render cond destroy failed "); free(descriptor); }
__unused JNIEXPORT void JNICALL Java_com_picsart_studio_gifencoder_GifInfoHandle_bindSurface(JNIEnv *env, jclass __unused handleClass, jlong gifInfo, jobject jsurface, jlongArray savedState, jboolean isOpaque) { GifInfo *info = (GifInfo *) (intptr_t) gifInfo; if (info->surfaceDescriptor == NULL) { info->surfaceDescriptor = malloc(sizeof(SurfaceDescriptor)); if (!initSurfaceDescriptor(info->surfaceDescriptor, env)) { free(info->surfaceDescriptor); info->surfaceDescriptor = NULL; return; } } POLL_TYPE eftd_ctr; int pollResult; while (1) { pollResult = poll(&info->surfaceDescriptor->eventPollFd, 1, 0); if (pollResult == 0) break; else if (pollResult > 0) { if (read(info->surfaceDescriptor->eventPollFd.fd, &eftd_ctr, POLL_TYPE_SIZE) != POLL_TYPE_SIZE) { throwException(env, ILLEGAL_STATE_EXCEPTION_ERRNO, "Read on flushing failed"); return; } } else { throwException(env, ILLEGAL_STATE_EXCEPTION_ERRNO, "Poll on flushing failed"); return; } } const int32_t windowFormat = isOpaque == JNI_TRUE ? WINDOW_FORMAT_RGBX_8888 : WINDOW_FORMAT_RGBA_8888; info->isOpaque = isOpaque; struct ANativeWindow *window = ANativeWindow_fromSurface(env, jsurface); if (ANativeWindow_setBuffersGeometry(window, (int32_t) info->gifFilePtr->SWidth, (int32_t) info->gifFilePtr->SHeight, windowFormat) != 0) { ANativeWindow_release(window); throwException(env, ILLEGAL_STATE_EXCEPTION_ERRNO, "Buffers geometry setting failed"); return; } struct ANativeWindow_Buffer buffer = {.bits =NULL}; void *oldBufferBits; if (ANativeWindow_lock(window, &buffer, NULL) != 0) { ANativeWindow_release(window); throwException(env, ILLEGAL_STATE_EXCEPTION_ERRNO, "Window lock failed"); return; } const size_t bufferSize = buffer.stride * buffer.height * sizeof(argb); info->stride = buffer.stride; long invalidationDelayMillis; if (info->surfaceDescriptor->surfaceBackupPtr) { memcpy(buffer.bits, info->surfaceDescriptor->surfaceBackupPtr, bufferSize); invalidationDelayMillis = 0; info->surfaceDescriptor->renderHelper = 1; info->surfaceDescriptor->slurpHelper = 0; } else { if (savedState != NULL){ invalidationDelayMillis = restoreSavedState(info, env, savedState, buffer.bits); if (invalidationDelayMillis <0) invalidationDelayMillis =0; } else invalidationDelayMillis = 0; info->surfaceDescriptor->renderHelper = 0; info->surfaceDescriptor->slurpHelper = 1; } info->lastFrameRemainder = -1; ANativeWindow_unlockAndPost(window); if (info->loopCount != 0 && info->currentLoop == info->loopCount) { ANativeWindow_release(window); pollResult = poll(&info->surfaceDescriptor->eventPollFd, 1, -1); if (pollResult < 0) { throwException(env, ILLEGAL_STATE_EXCEPTION_ERRNO, "animation end poll failed"); } return; } pthread_t thread; if (pthread_create(&thread, NULL, slurp, info) != 0) { ANativeWindow_release(window); throwException(env, ILLEGAL_STATE_EXCEPTION_ERRNO, "pthread_create failed"); return; } while (1) { pollResult = poll(&info->surfaceDescriptor->eventPollFd, 1, (int) invalidationDelayMillis); long renderingStartTime = getRealTime(); if (pollResult < 0) { throwException(env, ILLEGAL_STATE_EXCEPTION_ERRNO, "Poll failed"); break; } else if (pollResult > 0) { if (info->surfaceDescriptor->surfaceBackupPtr == NULL) { info->surfaceDescriptor->surfaceBackupPtr = malloc(bufferSize); if (info->surfaceDescriptor->surfaceBackupPtr == NULL) { throwException(env, OUT_OF_MEMORY_ERROR, OOME_MESSAGE); break; } } memcpy(info->surfaceDescriptor->surfaceBackupPtr, buffer.bits, bufferSize); break; } oldBufferBits = buffer.bits; THROW_AND_BREAK_ON_NONZERO_RESULT(ANativeWindow_lock(window, &buffer, NULL), "Window lock failed"); if (info->currentIndex == 0) prepareCanvas(buffer.bits, info); else memcpy(buffer.bits, oldBufferBits, bufferSize); pthread_mutex_lock(&info->surfaceDescriptor->renderMutex); while (info->surfaceDescriptor->renderHelper == 0) { pthread_cond_wait(&info->surfaceDescriptor->renderCond, &info->surfaceDescriptor->renderMutex); } info->surfaceDescriptor->renderHelper = 0; pthread_mutex_unlock(&info->surfaceDescriptor->renderMutex); const uint_fast32_t frameDuration = getBitmap(buffer.bits, info); pthread_mutex_lock(&info->surfaceDescriptor->slurpMutex); info->surfaceDescriptor->slurpHelper = 1; pthread_cond_signal(&info->surfaceDescriptor->slurpCond); pthread_mutex_unlock(&info->surfaceDescriptor->slurpMutex); ANativeWindow_unlockAndPost(window); invalidationDelayMillis = calculateInvalidationDelay(info, renderingStartTime, frameDuration); if (info->lastFrameRemainder >= 0) { invalidationDelayMillis = info->lastFrameRemainder; info->lastFrameRemainder = -1; } } ANativeWindow_release(window); pthread_mutex_lock(&info->surfaceDescriptor->slurpMutex); info->surfaceDescriptor->slurpHelper = 2; pthread_cond_signal(&info->surfaceDescriptor->slurpCond); pthread_mutex_unlock(&info->surfaceDescriptor->slurpMutex); THROW_ON_NONZERO_RESULT(pthread_join(thread, NULL), "join failed"); }
__unused JNIEXPORT void JNICALL Java_pl_droidsonroids_gif_GifInfoHandle_bindSurface(JNIEnv *env, jclass __unused handleClass, jlong gifInfo, jobject jsurface, jlongArray savedState) { GifInfo *info = (GifInfo *) (intptr_t) gifInfo; SurfaceDescriptor *descriptor = info->frameBufferDescriptor; if (descriptor == NULL) { descriptor = malloc(sizeof(SurfaceDescriptor)); if (descriptor == NULL) { throwException(env, OUT_OF_MEMORY_ERROR, OOME_MESSAGE); return; } descriptor->eventPollFd.events = POLL_IN; descriptor->eventPollFd.fd = eventfd(0, 0); if (descriptor->eventPollFd.fd == -1) { throwException(env, RUNTIME_EXCEPTION_ERRNO, "Eventfd creation failed "); free(descriptor); return; } errno = pthread_cond_init(&descriptor->slurpCond, NULL); THROW_ON_NONZERO_RESULT(errno, "Slurp condition variable initialization failed "); errno = pthread_cond_init(&descriptor->renderCond, NULL); THROW_ON_NONZERO_RESULT(errno, "Render condition variable initialization failed "); errno = pthread_mutex_init(&descriptor->slurpMutex, NULL); THROW_ON_NONZERO_RESULT(errno, "Slurp mutex initialization failed "); errno = pthread_mutex_init(&descriptor->renderMutex, NULL); THROW_ON_NONZERO_RESULT(errno, "Render mutex initialization failed "); descriptor->frameBuffer = NULL; info->frameBufferDescriptor = descriptor; info->destructor = releaseSurfaceDescriptor; } eventfd_t eventValue; int pollResult; while (1) { pollResult = TEMP_FAILURE_RETRY(poll(&descriptor->eventPollFd, 1, 0)); if (pollResult == 0) break; else if (pollResult > 0) { const int readResult = TEMP_FAILURE_RETRY(eventfd_read(descriptor->eventPollFd.fd, &eventValue)); if (readResult != 0) { throwException(env, RUNTIME_EXCEPTION_ERRNO, "Could not read from eventfd "); return; } } else { throwException(env, RUNTIME_EXCEPTION_ERRNO, "Could not poll on eventfd "); return; } } const int32_t windowFormat = info->isOpaque ? WINDOW_FORMAT_RGBX_8888 : WINDOW_FORMAT_RGBA_8888; struct ANativeWindow *window = ANativeWindow_fromSurface(env, jsurface); GifFileType *const gifFilePtr = info->gifFilePtr; if (ANativeWindow_setBuffersGeometry(window, (int32_t) gifFilePtr->SWidth, (int32_t) gifFilePtr->SHeight, windowFormat) != 0) { ANativeWindow_release(window); throwException(env, RUNTIME_EXCEPTION_ERRNO, "Buffers geometry setting failed "); return; } struct ANativeWindow_Buffer buffer = {.bits =NULL}; void *oldBufferBits; if (ANativeWindow_lock(window, &buffer, NULL) != 0) { #ifdef DEBUG LOGE("Window lock failed %d", errno); #endif ANativeWindow_release(window); return; } const size_t bufferSize = buffer.stride * buffer.height * sizeof(argb); info->stride = buffer.stride; long long invalidationDelayMillis; if (descriptor->frameBuffer) { memcpy(buffer.bits, descriptor->frameBuffer, bufferSize); invalidationDelayMillis = 0; descriptor->renderHelper = 1; descriptor->slurpHelper = 0; } else { if (savedState != NULL) { invalidationDelayMillis = restoreSavedState(info, env, savedState, buffer.bits); if (invalidationDelayMillis < 0) invalidationDelayMillis = 0; } else invalidationDelayMillis = 0; descriptor->renderHelper = 0; descriptor->slurpHelper = 1; } info->lastFrameRemainder = -1; ANativeWindow_unlockAndPost(window); if (info->loopCount != 0 && info->currentLoop == info->loopCount) { ANativeWindow_release(window); pollResult = TEMP_FAILURE_RETRY(poll(&descriptor->eventPollFd, 1, -1)); if (pollResult < 0) { throwException(env, RUNTIME_EXCEPTION_ERRNO, "Animation end poll failed "); } return; } errno = pthread_create(&descriptor->slurpThread, NULL, slurp, info); if (errno != 0) { throwException(env, RUNTIME_EXCEPTION_ERRNO, "Slurp thread creation failed "); ANativeWindow_release(window); return; } while (1) { pollResult = TEMP_FAILURE_RETRY(poll(&descriptor->eventPollFd, 1, (int) invalidationDelayMillis)); long renderingStartTime = getRealTime(); if (pollResult < 0) { throwException(env, RUNTIME_EXCEPTION_ERRNO, "Display loop poll failed "); break; } else if (pollResult > 0) { if (descriptor->frameBuffer == NULL) { descriptor->frameBuffer = malloc(bufferSize); if (descriptor->frameBuffer == NULL) { throwException(env, OUT_OF_MEMORY_ERROR, OOME_MESSAGE); break; } } memcpy(descriptor->frameBuffer, buffer.bits, bufferSize); break; } oldBufferBits = buffer.bits; struct ARect *dirtyRectPtr; if (info->currentIndex == 0) { dirtyRectPtr = NULL; } else { const GifImageDesc imageDesc = gifFilePtr->SavedImages[info->currentIndex].ImageDesc; struct ARect dirtyRect = { .left = imageDesc.Left, .top = imageDesc.Top, .right = imageDesc.Left + imageDesc.Width, .bottom = imageDesc.Top + imageDesc.Height }; dirtyRectPtr = &dirtyRect; } if (ANativeWindow_lock(window, &buffer, dirtyRectPtr) != 0) { #ifdef DEBUG LOGE("Window lock failed %d", errno); #endif break; } if (info->currentIndex == 0) prepareCanvas(buffer.bits, info); else memcpy(buffer.bits, oldBufferBits, bufferSize); pthread_mutex_lock(&descriptor->renderMutex); while (descriptor->renderHelper == 0) { pthread_cond_wait(&descriptor->renderCond, &descriptor->renderMutex); } descriptor->renderHelper = 0; pthread_mutex_unlock(&descriptor->renderMutex); const uint_fast32_t frameDuration = getBitmap(buffer.bits, info); pthread_mutex_lock(&descriptor->slurpMutex); descriptor->slurpHelper = 1; pthread_cond_signal(&descriptor->slurpCond); pthread_mutex_unlock(&descriptor->slurpMutex); ANativeWindow_unlockAndPost(window); invalidationDelayMillis = calculateInvalidationDelay(info, renderingStartTime, frameDuration); if (info->lastFrameRemainder >= 0) { invalidationDelayMillis = info->lastFrameRemainder; info->lastFrameRemainder = -1; } } ANativeWindow_release(window); pthread_mutex_lock(&descriptor->slurpMutex); descriptor->slurpHelper = 2; pthread_cond_signal(&descriptor->slurpCond); pthread_mutex_unlock(&descriptor->slurpMutex); errno = pthread_join(descriptor->slurpThread, NULL); THROW_ON_NONZERO_RESULT(errno, "Slurp thread join failed"); } __unused JNIEXPORT void JNICALL Java_pl_droidsonroids_gif_GifInfoHandle_postUnbindSurface(JNIEnv *env, jclass __unused handleClass, jlong gifInfo) { GifInfo *info = (GifInfo *) (intptr_t) gifInfo; if (info == NULL || info->frameBufferDescriptor == NULL) { return; } SurfaceDescriptor const *descriptor = info->frameBufferDescriptor; const int writeResult = TEMP_FAILURE_RETRY(eventfd_write(descriptor->eventPollFd.fd, 1)); if (writeResult != 0 && errno != EBADF) { throwException(env, RUNTIME_EXCEPTION_ERRNO, "Could not write to eventfd "); } }