diff --git a/android/build.gradle b/android/build.gradle index bd3892a..26ac886 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -34,6 +34,6 @@ task clean(type: Delete) { } ext { - burstLinkerVer = '0.0.11' + burstLinkerVer = '0.0.12' libs = ['burstLinkerVer': "com.bilibili:$burstLinkerVer"] } diff --git a/android/lib/src/main/cpp/BurstLinker.cpp b/android/lib/src/main/cpp/BurstLinker.cpp index 489e526..e7dd3a9 100644 --- a/android/lib/src/main/cpp/BurstLinker.cpp +++ b/android/lib/src/main/cpp/BurstLinker.cpp @@ -1 +1 @@ -// // Created by succlz123 on 17-9-5. // #include #include #include #include #include #include "../../../../../src/GifEncoder.h" #include "../../../../../src/Logger.h" #include "../../../../../src/ThreadPool.h" #ifdef __cplusplus extern "C" { #endif #define RGB565_R(p) ((((p) & 0xF800) >> 11) << 3) #define RGB565_G(p) ((((p) & 0x7E0) >> 5) << 2) #define RGB565_B(p) (((p) & 0x1F) << 3) JNIEXPORT jlong JNICALL Java_com_bilibili_burstlinker_BurstLinker_jniInit(JNIEnv *env, jobject, jstring path, jint width, jint height, jint loopCount, jint threadCount) { const char *pathStr = env->GetStringUTFChars(path, nullptr); if (pathStr == nullptr) { return 0; } auto *gifEncoder = new blk::GifEncoder(); bool success = gifEncoder->init(pathStr, (uint16_t) width, (uint16_t) height, (uint32_t) loopCount, (uint32_t) threadCount); env->ReleaseStringUTFChars(path, pathStr); if (success) { return (jlong) gifEncoder; } else { delete gifEncoder; return 0; } } JNIEXPORT void JNICALL Java_com_bilibili_burstlinker_BurstLinker_jniDebugLog(JNIEnv *env, jobject, jlong handle, jboolean debug) { blk::GifEncoder *gifEncoder = (blk::GifEncoder *) handle; gifEncoder->debugLog = debug; } JNIEXPORT jstring JNICALL Java_com_bilibili_burstlinker_BurstLinker_jniConnect(JNIEnv *env, jobject, jlong handle, jint quantizerType, jint ditherType, jint ignoreTranslucency, jint left, jint top, jint delay, jstring rsCacheDir, jobject jBitmap) { if (jBitmap == nullptr) { return env->NewStringUTF("jBitmap is null"); } auto *gifEncoder = (blk::GifEncoder *) handle; AndroidBitmapInfo androidBitmapInfo; if (AndroidBitmap_getInfo(env, jBitmap, &androidBitmapInfo) < 0) { return env->NewStringUTF("call AndroidBitmap_getInfo failed"); } void *src = nullptr; if (AndroidBitmap_lockPixels(env, jBitmap, &src) < 0) { return env->NewStringUTF("call AndroidBitmap_lockPixels failed"); } char *rsCacheDirStr = nullptr; if (rsCacheDir != nullptr) { const char *tmp = env->GetStringUTFChars(rsCacheDir, nullptr); rsCacheDirStr = new char[strlen(tmp)]; strcpy(rsCacheDirStr, tmp); gifEncoder->rsCacheDir = rsCacheDirStr; env->ReleaseStringUTFChars(rsCacheDir, tmp); } uint16_t width = gifEncoder->screenWidth; uint16_t height = gifEncoder->screenHeight; uint32_t imageSize = width * height; std::vector dst(imageSize); int32_t stride = 0; int enableTransparency; if (androidBitmapInfo.format == ANDROID_BITMAP_FORMAT_RGB_565) { stride = width * 2; int32_t pixelsCount = stride * height; uint16_t *tmp = new uint16_t[imageSize]; memcpy(tmp, src, static_cast(pixelsCount)); AndroidBitmap_unlockPixels(env, jBitmap); for (int k = 0; k < imageSize; ++k) { uint16_t v = tmp[k]; dst.push_back(RGB565_B(v) << 16 | RGB565_G(v) << 8 | RGB565_R(v)); } delete[] tmp; enableTransparency = 0; } else if (androidBitmapInfo.format == ANDROID_BITMAP_FORMAT_RGBA_8888) { stride = width * 4; int32_t pixelsCount = stride * height; memcpy((void *) &dst[0], src, static_cast(pixelsCount)); AndroidBitmap_unlockPixels(env, jBitmap); enableTransparency = 1; } else { return env->NewStringUTF("bitmap's format is't RGB_565 or RGBA_8888"); } int transparencyOption = (ignoreTranslucency << 8) | enableTransparency; std::vector out; gifEncoder->addImage(dst, delay, static_cast(quantizerType), static_cast(ditherType), transparencyOption, (uint16_t) left, (uint16_t) top, out); if (out.size() <= 0) { return env->NewStringUTF("gifEncoder add image out arrays is empty"); } gifEncoder->flush(out); return nullptr; } JNIEXPORT jstring JNICALL Java_com_bilibili_burstlinker_BurstLinker_jniConnectArray(JNIEnv *env, jobject, jlong handle, jint quantizerType, jint ditherType, jint ignoreTranslucency, jint left, jint top, jint delay, jstring rsCacheDir, jobjectArray jBitmapArray) { auto *gifEncoder = (blk::GifEncoder *) handle; char *rsCacheDirStr = nullptr; if (rsCacheDir != nullptr) { const char *tmp = env->GetStringUTFChars(rsCacheDir, nullptr); rsCacheDirStr = new char[strlen(tmp)]; strcpy(rsCacheDirStr, tmp); gifEncoder->rsCacheDir = rsCacheDirStr; env->ReleaseStringUTFChars(rsCacheDir, tmp); } std::vector>> tasks; jsize count = env->GetArrayLength(jBitmapArray); for (int i = 0; i < count; i++) { jobject jBitmap = env->GetObjectArrayElement(jBitmapArray, i); if (jBitmap == nullptr) { return env->NewStringUTF("jBitmap is null"); } AndroidBitmapInfo androidBitmapInfo; if (AndroidBitmap_getInfo(env, jBitmap, &androidBitmapInfo) < 0) { env->DeleteLocalRef(jBitmap); return env->NewStringUTF("call AndroidBitmap_getInfo failed"); } void *src = nullptr; if (AndroidBitmap_lockPixels(env, jBitmap, &src) < 0) { env->DeleteLocalRef(jBitmap); return env->NewStringUTF("call AndroidBitmap_lockPixels failed"); } uint16_t width = gifEncoder->screenWidth; uint16_t height = gifEncoder->screenHeight; uint32_t imageSize = width * height; std::vector dst(imageSize); int32_t stride = 0; int enableTransparency; if (androidBitmapInfo.format == ANDROID_BITMAP_FORMAT_RGB_565) { stride = width * 2; int32_t pixelsCount = stride * height; uint16_t *tmp = new uint16_t[imageSize]; memcpy(tmp, src, static_cast(pixelsCount)); AndroidBitmap_unlockPixels(env, jBitmap); for (int k = 0; k < imageSize; ++k) { uint16_t v = tmp[k]; dst.push_back(RGB565_B(v) << 16 | RGB565_G(v) << 8 | RGB565_R(v)); } delete[] tmp; enableTransparency = 0; } else if (androidBitmapInfo.format == ANDROID_BITMAP_FORMAT_RGBA_8888) { stride = width * 4; int32_t pixelsCount = stride * height; memcpy((void *) &dst[0], src, static_cast(pixelsCount)); AndroidBitmap_unlockPixels(env, jBitmap); enableTransparency = 1; } else { env->DeleteLocalRef(jBitmap); return env->NewStringUTF("bitmap's format is't RGB_565 or RGBA_8888"); } env->DeleteLocalRef(jBitmap); int transparencyOption = (ignoreTranslucency << 8) | enableTransparency; auto result = gifEncoder->threadPool->enqueue([=]() { std::vector out; gifEncoder->addImage(dst, delay, static_cast(quantizerType), static_cast(ditherType), transparencyOption, (uint16_t) left, (uint16_t) top, out); return out; }); tasks.emplace_back(std::move(result)); } for (auto &task : tasks) { std::vector out = task.get(); if (out.size() <= 0) { return env->NewStringUTF("gifEncoder add image out arrays is empty"); } gifEncoder->flush(out); } return nullptr; } JNIEXPORT void JNICALL Java_com_bilibili_burstlinker_BurstLinker_jniRelease(JNIEnv *env, jobject, jlong handle) { blk::GifEncoder *gifEncoder = (blk::GifEncoder *) handle; gifEncoder->finishEncoding(); delete gifEncoder; } #ifdef __cplusplus } #endif \ No newline at end of file +// // Created by succlz123 on 17-9-5. // #include #include #include #include #include #include "../../../../../src/GifEncoder.h" #include "../../../../../src/Logger.h" #include "../../../../../src/ThreadPool.h" #ifdef __cplusplus extern "C" { #endif #define RGB565_R(p) ((((p) & 0xF800) >> 11) << 3) #define RGB565_G(p) ((((p) & 0x7E0) >> 5) << 2) #define RGB565_B(p) (((p) & 0x1F) << 3) JNIEXPORT jlong JNICALL Java_com_bilibili_burstlinker_BurstLinker_jniInit(JNIEnv *env, jobject, jstring path, jint width, jint height, jint loopCount, jint threadCount) { const char *pathStr = env->GetStringUTFChars(path, nullptr); if (pathStr == nullptr) { return 0; } auto *gifEncoder = new blk::GifEncoder(); bool success = gifEncoder->init(pathStr, (uint16_t) width, (uint16_t) height, (uint32_t) loopCount, (uint32_t) threadCount); env->ReleaseStringUTFChars(path, pathStr); if (success) { return (jlong) gifEncoder; } else { delete gifEncoder; return 0; } } JNIEXPORT void JNICALL Java_com_bilibili_burstlinker_BurstLinker_jniDebugLog(JNIEnv *env, jobject, jlong handle, jboolean debug) { blk::GifEncoder *gifEncoder = (blk::GifEncoder *) handle; gifEncoder->debugLog = debug; } JNIEXPORT jstring JNICALL Java_com_bilibili_burstlinker_BurstLinker_jniConnect(JNIEnv *env, jobject, jlong handle, jint quantizerType, jint ditherType, jint ignoreTranslucency, jint left, jint top, jint delay, jstring rsCacheDir, jobject jBitmap) { if (jBitmap == nullptr) { return env->NewStringUTF("jBitmap is null"); } auto *gifEncoder = (blk::GifEncoder *) handle; AndroidBitmapInfo androidBitmapInfo; if (AndroidBitmap_getInfo(env, jBitmap, &androidBitmapInfo) < 0) { return env->NewStringUTF("call AndroidBitmap_getInfo failed"); } void *src = nullptr; if (AndroidBitmap_lockPixels(env, jBitmap, &src) < 0) { return env->NewStringUTF("call AndroidBitmap_lockPixels failed"); } char *rsCacheDirStr = nullptr; if (rsCacheDir != nullptr) { const char *tmp = env->GetStringUTFChars(rsCacheDir, nullptr); rsCacheDirStr = new char[strlen(tmp)]; strcpy(rsCacheDirStr, tmp); gifEncoder->rsCacheDir = rsCacheDirStr; env->ReleaseStringUTFChars(rsCacheDir, tmp); } uint16_t width = gifEncoder->screenWidth; uint16_t height = gifEncoder->screenHeight; uint32_t imageSize = width * height; std::vector dst(imageSize); int32_t stride = 0; int enableTransparency; if (androidBitmapInfo.format == ANDROID_BITMAP_FORMAT_RGB_565) { stride = width * 2; int32_t pixelsCount = stride * height; uint16_t *tmp = new uint16_t[imageSize]; memcpy(tmp, src, static_cast(pixelsCount)); AndroidBitmap_unlockPixels(env, jBitmap); for (int k = 0; k < imageSize; ++k) { uint16_t v = tmp[k]; dst.push_back(RGB565_B(v) << 16 | RGB565_G(v) << 8 | RGB565_R(v)); } delete[] tmp; enableTransparency = 0; } else if (androidBitmapInfo.format == ANDROID_BITMAP_FORMAT_RGBA_8888) { stride = width * 4; int32_t pixelsCount = stride * height; memcpy((void *) &dst[0], src, static_cast(pixelsCount)); AndroidBitmap_unlockPixels(env, jBitmap); enableTransparency = 1; } else { return env->NewStringUTF("bitmap's format is't RGB_565 or RGBA_8888"); } int transparencyOption = (ignoreTranslucency << 8) | enableTransparency; std::vector out; gifEncoder->addImage(dst, delay, static_cast(quantizerType), static_cast(ditherType), transparencyOption, (uint16_t) left, (uint16_t) top, out); if (out.size() <= 0) { return env->NewStringUTF("gifEncoder add image out arrays is empty"); } gifEncoder->flush(out); return nullptr; } JNIEXPORT jstring JNICALL Java_com_bilibili_burstlinker_BurstLinker_jniConnectArray(JNIEnv *env, jobject, jlong handle, jint quantizerType, jint ditherType, jint ignoreTranslucency, jint left, jint top, jint delay, jstring rsCacheDir, jobjectArray jBitmapArray) { auto *gifEncoder = (blk::GifEncoder *) handle; char *rsCacheDirStr = nullptr; if (rsCacheDir != nullptr) { const char *tmp = env->GetStringUTFChars(rsCacheDir, nullptr); rsCacheDirStr = new char[strlen(tmp)]; strcpy(rsCacheDirStr, tmp); gifEncoder->rsCacheDir = rsCacheDirStr; env->ReleaseStringUTFChars(rsCacheDir, tmp); } std::vector>> tasks; jsize count = env->GetArrayLength(jBitmapArray); for (int i = 0; i < count; i++) { jobject jBitmap = env->GetObjectArrayElement(jBitmapArray, i); if (jBitmap == nullptr) { return env->NewStringUTF("jBitmap is null"); } AndroidBitmapInfo androidBitmapInfo; if (AndroidBitmap_getInfo(env, jBitmap, &androidBitmapInfo) < 0) { env->DeleteLocalRef(jBitmap); return env->NewStringUTF("call AndroidBitmap_getInfo failed"); } void *src = nullptr; if (AndroidBitmap_lockPixels(env, jBitmap, &src) < 0) { env->DeleteLocalRef(jBitmap); return env->NewStringUTF("call AndroidBitmap_lockPixels failed"); } uint16_t width = gifEncoder->screenWidth; uint16_t height = gifEncoder->screenHeight; uint32_t imageSize = width * height; std::vector dst(imageSize); int32_t stride = 0; int enableTransparency; if (androidBitmapInfo.format == ANDROID_BITMAP_FORMAT_RGB_565) { stride = width * 2; int32_t pixelsCount = stride * height; uint16_t *tmp = new uint16_t[imageSize]; memcpy(tmp, src, static_cast(pixelsCount)); AndroidBitmap_unlockPixels(env, jBitmap); for (int k = 0; k < imageSize; ++k) { uint16_t v = tmp[k]; dst.push_back(RGB565_B(v) << 16 | RGB565_G(v) << 8 | RGB565_R(v)); } delete[] tmp; enableTransparency = 0; } else if (androidBitmapInfo.format == ANDROID_BITMAP_FORMAT_RGBA_8888) { stride = width * 4; int32_t pixelsCount = stride * height; memcpy((void *) &dst[0], src, static_cast(pixelsCount)); AndroidBitmap_unlockPixels(env, jBitmap); enableTransparency = 1; } else { env->DeleteLocalRef(jBitmap); return env->NewStringUTF("bitmap's format is't RGB_565 or RGBA_8888"); } env->DeleteLocalRef(jBitmap); int transparencyOption = (ignoreTranslucency << 8) | enableTransparency; auto result = gifEncoder->threadPool->enqueue([=]() { std::vector out; gifEncoder->addImage(dst, delay, static_cast(quantizerType), static_cast(ditherType), transparencyOption, (uint16_t) left, (uint16_t) top, out); return out; }); tasks.emplace_back(std::move(result)); } for (auto &task : tasks) { std::vector out = task.get(); if (out.size() <= 0) { return env->NewStringUTF("gifEncoder add image out arrays is empty"); } gifEncoder->flush(out); } return nullptr; } JNIEXPORT void JNICALL Java_com_bilibili_burstlinker_BurstLinker_jniRelease(JNIEnv *env, jobject, jlong handle) { blk::GifEncoder *gifEncoder = (blk::GifEncoder *) handle; if (gifEncoder != nullptr) { gifEncoder->finishEncoding(); delete gifEncoder; } } #ifdef __cplusplus } #endif \ No newline at end of file diff --git a/android/lib/src/main/java/com/bilibili/burstlinker/BurstLinker.java b/android/lib/src/main/java/com/bilibili/burstlinker/BurstLinker.java index 1972ec1..8977ce7 100644 --- a/android/lib/src/main/java/com/bilibili/burstlinker/BurstLinker.java +++ b/android/lib/src/main/java/com/bilibili/burstlinker/BurstLinker.java @@ -206,7 +206,7 @@ public void release() { if (mNative == 0) { return; } -// jniRelease(mNative); + jniRelease(mNative); mNative = 0; } } diff --git a/example/Main.cpp b/example/Main.cpp index 993dc4e..c7d4c95 100644 --- a/example/Main.cpp +++ b/example/Main.cpp @@ -63,7 +63,7 @@ void addImage(const char *fileName, uint32_t width, uint32_t height, uint32_t de std::vector> images; images.emplace_back(image); images.emplace_back(image); - burstLinker.connect(images, delay, quantizerType, ditherType, transparencyOption, 0, 0); + burstLinker.connect(images, delay, quantizerType, ditherType, transparencyOption, 0, 0); } void diff --git a/src/Ditherer.h b/src/Ditherer.h index 932024d..9ab10e3 100644 --- a/src/Ditherer.h +++ b/src/Ditherer.h @@ -25,9 +25,9 @@ namespace blk { // only for bayer int bayerScale = 1; - uint16_t width; + uint16_t width = 0; - uint16_t height; + uint16_t height = 0; virtual ~Ditherer() = default; diff --git a/src/GifEncoder.cpp b/src/GifEncoder.cpp index a7eaab7..a574ed8 100644 --- a/src/GifEncoder.cpp +++ b/src/GifEncoder.cpp @@ -125,9 +125,6 @@ std::vector GifEncoder::addImage(const std::vector &original, bool ignoreTranslucency = (((transparencyOption >> 8) & 0xff) == 1); bool hasTransparentColor = false; uint8_t a = 255; - uint8_t r; - uint8_t g; - uint8_t b; for (uint32_t i = 0; i < size; i++) { auto color = original[i]; if (enableTransparentColor) { @@ -138,9 +135,9 @@ std::vector GifEncoder::addImage(const std::vector &original, } } } - b = static_cast((color >> 16) & 0xff); - g = static_cast((color >> 8) & 0xff); - r = static_cast(color & 0xff); + auto b = static_cast((color >> 16) & 0xff); + auto g = static_cast((color >> 8) & 0xff); + auto r = static_cast(color & 0xff); if (a == 255 || (!ignoreTranslucency && a != 0)) { quantizeIn.emplace_back(a, r, g, b, i); } @@ -150,8 +147,7 @@ std::vector GifEncoder::addImage(const std::vector &original, quantizeOut.reserve(256); int quantizeSize = 0; if (size > 256) { - quantizeSize = colorQuantizer->quantize(quantizeIn, hasTransparentColor ? 255 : 256, - quantizeOut); + quantizeSize = colorQuantizer->quantize(quantizeIn, hasTransparentColor ? 255 : 256, quantizeOut); } else { quantizeSize = size; quantizeOut.assign(quantizeIn.begin(), quantizeIn.end()); diff --git a/src/OctreeQuantizer.cpp b/src/OctreeQuantizer.cpp index e1c9342..286abff 100644 --- a/src/OctreeQuantizer.cpp +++ b/src/OctreeQuantizer.cpp @@ -60,8 +60,6 @@ OctreeQuantizer::Node *OctreeQuantizer::createNode(int inLevel) { } bool OctreeQuantizer::addColor(Node **node, uint32_t r, uint32_t g, uint32_t b, int level) { - int index, shift; - if (*node == nullptr) { *node = createNode(level); } @@ -75,10 +73,10 @@ bool OctreeQuantizer::addColor(Node **node, uint32_t r, uint32_t g, uint32_t b, (*node)->gSum += g; (*node)->bSum += b; } else { - shift = 7 - level; - index = (((r & mask[level]) >> shift) << 2) - | (((g & mask[level]) >> shift) << 1) - | ((b & mask[level]) >> shift); + int shift = 7 - level; + int index = (((r & mask[level]) >> shift) << 2) + | (((g & mask[level]) >> shift) << 1) + | ((b & mask[level]) >> shift); if (!addColor(&((*node)->child[index]), r, g, b, level + 1)) { return false; }