diff --git a/README.md b/README.md index ceffcdc..0f5e93c 100644 --- a/README.md +++ b/README.md @@ -31,7 +31,7 @@ This is all done without decoding the JPEG to RGB. All operations on the JPEG ar Add __JpegKit__ to the dependencies block in your `app` level `build.gradle`: ```groovy -compile 'com.camerakit:jpegkit:0.0.1' +compile 'com.camerakit:jpegkit:0.2.0' ``` ## Usage diff --git a/app/CMakeLists.txt b/app/CMakeLists.txt new file mode 100644 index 0000000..e2c893b --- /dev/null +++ b/app/CMakeLists.txt @@ -0,0 +1,8 @@ +cmake_minimum_required(VERSION 3.4.1) + +add_library(demo SHARED src/main/cpp/demo.cpp) + +find_library(log-lib log) +find_library(android-lib android) + +target_link_libraries(demo ${log-lib} ${android-lib}) diff --git a/app/build.gradle b/app/build.gradle index 649ac5a..d63ce2f 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -1,17 +1,37 @@ -apply plugin: 'com.android.application' +plugins { + id 'com.android.application' + id 'kotlin-android' +} android { - compileSdkVersion 26 + compileSdkVersion = 28 + buildToolsVersion = '28.0.0' defaultConfig { - applicationId 'com.jpegkit.app' + applicationId 'jpegkit.app' minSdkVersion 15 - targetSdkVersion 26 + targetSdkVersion 28 versionCode 1 - versionName '0.1.0' + versionName '0.2.0' + externalNativeBuild { + cmake { + cppFlags '' + } + } + } + externalNativeBuild { + cmake { + path 'CMakeLists.txt' + } } } +repositories { + jcenter() + google() +} + dependencies { implementation project(':jpegkit') - implementation 'com.android.support:appcompat-v7:26.1.0' + implementation 'org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.2.50' + implementation 'com.android.support:appcompat-v7:28.0.0-alpha3' } diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index cbfcc2c..aba13b1 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -1,6 +1,6 @@ + package="jpegkit.app"> diff --git a/jpegkit/src/main/assets/cat.jpg b/app/src/main/assets/cat.jpg similarity index 100% rename from jpegkit/src/main/assets/cat.jpg rename to app/src/main/assets/cat.jpg diff --git a/app/src/main/cpp/demo.cpp b/app/src/main/cpp/demo.cpp new file mode 100644 index 0000000..8336e89 --- /dev/null +++ b/app/src/main/cpp/demo.cpp @@ -0,0 +1,5 @@ +#include + +extern "C" JNIEXPORT void JNICALL +Java_com_jpegkit_app_MainActivity_init(JNIEnv *env, jobject obj) { +} \ No newline at end of file diff --git a/app/src/main/java/com/jpegkit/app/JpegActivity.java b/app/src/main/java/jpegkit/app/JpegActivity.java similarity index 99% rename from app/src/main/java/com/jpegkit/app/JpegActivity.java rename to app/src/main/java/jpegkit/app/JpegActivity.java index 7eba20c..5ac4c27 100644 --- a/app/src/main/java/com/jpegkit/app/JpegActivity.java +++ b/app/src/main/java/jpegkit/app/JpegActivity.java @@ -1,4 +1,4 @@ -package com.jpegkit.app; +package jpegkit.app; import android.content.Context; import android.content.DialogInterface; @@ -21,8 +21,8 @@ import android.widget.TextView; import android.widget.Toast; -import com.jpegkit.JpegFile; -import com.jpegkit.JpegImageView; +import jpegkit.JpegFile; +import jpegkit.JpegImageView; import java.util.Set; import java.util.TreeSet; diff --git a/app/src/main/java/com/jpegkit/app/MainActivity.java b/app/src/main/java/jpegkit/app/MainActivity.java similarity index 99% rename from app/src/main/java/com/jpegkit/app/MainActivity.java rename to app/src/main/java/jpegkit/app/MainActivity.java index 61352b6..6c29e3f 100644 --- a/app/src/main/java/com/jpegkit/app/MainActivity.java +++ b/app/src/main/java/jpegkit/app/MainActivity.java @@ -1,4 +1,4 @@ -package com.jpegkit.app; +package jpegkit.app; import android.Manifest; import android.content.Intent; @@ -23,9 +23,9 @@ import android.widget.TextView; import android.widget.Toast; -import com.jpegkit.Jpeg; -import com.jpegkit.JpegFile; -import com.jpegkit.JpegImageView; +import jpegkit.Jpeg; +import jpegkit.JpegFile; +import jpegkit.JpegImageView; import java.io.File; import java.io.FileOutputStream; diff --git a/app/src/main/res/layout/activity_jpeg.xml b/app/src/main/res/layout/activity_jpeg.xml index e5deb6e..a3e7ffa 100644 --- a/app/src/main/res/layout/activity_jpeg.xml +++ b/app/src/main/res/layout/activity_jpeg.xml @@ -4,7 +4,7 @@ android:layout_height="match_parent" android:background="#EEEEEE"> - - @@ -30,7 +41,7 @@ ext.getBintrayUser = { -> Properties properties = new Properties() properties.load(rootProject.file('local.properties').newDataInputStream()) - return properties.getProperty('bintray.user'); + return properties.getProperty('bintray.user') } ext.getBintrayKey = { -> @@ -100,7 +111,7 @@ task deployRelease { } group = 'com.camerakit' -version = '0.1.0' +version = '0.2.0' install { repositories.mavenInstaller { diff --git a/jpegkit/src/main/CMakeLists.txt b/jpegkit/src/main/CMakeLists.txt deleted file mode 100644 index c857cbd..0000000 --- a/jpegkit/src/main/CMakeLists.txt +++ /dev/null @@ -1,13 +0,0 @@ -cmake_minimum_required(VERSION 3.4.1) - -set(JPEG_INC_DIR ${CMAKE_SOURCE_DIR}/cpp/libjpeg/include) -set(JPEG_BUILD_DIR ${CMAKE_SOURCE_DIR}/jniLibs) - -include_directories(${JPEG_INC_DIR}) - -add_library(libjpeg STATIC IMPORTED) -set_target_properties(libjpeg PROPERTIES IMPORTED_LOCATION ${JPEG_BUILD_DIR}/${ANDROID_ABI}/libjpeg-turbo.a) - -add_library(jpegkit SHARED ${CMAKE_SOURCE_DIR}/cpp/JniJpeg.cpp) - -target_link_libraries(jpegkit libjpeg) diff --git a/jpegkit/src/main/cpp/jpegkit/include/platform.h b/jpegkit/src/main/cpp/jpegkit/include/platform.h new file mode 100644 index 0000000..f7f28af --- /dev/null +++ b/jpegkit/src/main/cpp/jpegkit/include/platform.h @@ -0,0 +1,11 @@ +#ifndef __JKUTILS_H__ +#define __JKUTILS_H__ + +#include +#include +#include +#include + +typedef struct { unsigned char *bytes; } Allocation; + +#endif \ No newline at end of file diff --git a/jpegkit/src/main/cpp/JniJpeg.cpp b/jpegkit/src/main/cpp/jpegkit/jpeg.cpp similarity index 87% rename from jpegkit/src/main/cpp/JniJpeg.cpp rename to jpegkit/src/main/cpp/jpegkit/jpeg.cpp index bcbb368..8c68dd6 100644 --- a/jpegkit/src/main/cpp/JniJpeg.cpp +++ b/jpegkit/src/main/cpp/jpegkit/jpeg.cpp @@ -7,43 +7,43 @@ extern "C" { JNIEXPORT jobject JNICALL -Java_com_jpegkit_Jpeg_jniMount +Java_jpegkit_Jpeg_jniMount (JNIEnv *env, jobject obj, jbyteArray jpegBytes); JNIEXPORT void JNICALL -Java_com_jpegkit_Jpeg_jniRelease +Java_jpegkit_Jpeg_jniRelease (JNIEnv *env, jobject obj, jobject handle); JNIEXPORT jbyteArray JNICALL -Java_com_jpegkit_Jpeg_jniGetJpegBytes +Java_jpegkit_Jpeg_jniGetJpegBytes (JNIEnv *env, jobject obj, jobject handle); JNIEXPORT jlong JNICALL -Java_com_jpegkit_Jpeg_jniGetJpegSize +Java_jpegkit_Jpeg_jniGetJpegSize (JNIEnv *env, jobject obj, jobject handle); JNIEXPORT jint JNICALL -Java_com_jpegkit_Jpeg_jniGetWidth +Java_jpegkit_Jpeg_jniGetWidth (JNIEnv *env, jobject obj, jobject handle); JNIEXPORT jint JNICALL -Java_com_jpegkit_Jpeg_jniGetHeight +Java_jpegkit_Jpeg_jniGetHeight (JNIEnv *env, jobject obj, jobject handle); JNIEXPORT void JNICALL -Java_com_jpegkit_Jpeg_jniRotate +Java_jpegkit_Jpeg_jniRotate (JNIEnv *env, jobject obj, jobject handle, jint degrees); JNIEXPORT void JNICALL -Java_com_jpegkit_Jpeg_jniFlipHorizontal +Java_jpegkit_Jpeg_jniFlipHorizontal (JNIEnv *env, jobject obj, jobject handle); JNIEXPORT void JNICALL -Java_com_jpegkit_Jpeg_jniFlipVertical +Java_jpegkit_Jpeg_jniFlipVertical (JNIEnv *env, jobject obj, jobject handle); JNIEXPORT void JNICALL -Java_com_jpegkit_Jpeg_jniCrop +Java_jpegkit_Jpeg_jniCrop (JNIEnv *env, jobject obj, jobject handle, jint left, jint top, jint right, jint bottom); } @@ -65,7 +65,7 @@ class Jpeg { }; JNIEXPORT jobject JNICALL -Java_com_jpegkit_Jpeg_jniMount +Java_jpegkit_Jpeg_jniMount (JNIEnv *env, jobject obj, jbyteArray jpegBytes) { int jpegSize = env->GetArrayLength(jpegBytes); @@ -84,7 +84,7 @@ Java_com_jpegkit_Jpeg_jniMount JNIEXPORT void JNICALL -Java_com_jpegkit_Jpeg_jniRelease +Java_jpegkit_Jpeg_jniRelease (JNIEnv *env, jobject obj, jobject handle) { Jpeg *jpeg = (Jpeg *) env->GetDirectBufferAddress(handle); @@ -100,7 +100,7 @@ Java_com_jpegkit_Jpeg_jniRelease } JNIEXPORT jbyteArray JNICALL -Java_com_jpegkit_Jpeg_jniGetJpegBytes +Java_jpegkit_Jpeg_jniGetJpegBytes (JNIEnv *env, jobject obj, jobject handle) { Jpeg *jpeg = (Jpeg *) env->GetDirectBufferAddress(handle); @@ -110,14 +110,14 @@ Java_com_jpegkit_Jpeg_jniGetJpegBytes } JNIEXPORT jlong JNICALL -Java_com_jpegkit_Jpeg_jniGetJpegSize +Java_jpegkit_Jpeg_jniGetJpegSize (JNIEnv *env, jobject obj, jobject handle) { Jpeg *jpeg = (Jpeg *) env->GetDirectBufferAddress(handle); return jpeg->size; } JNIEXPORT jint JNICALL -Java_com_jpegkit_Jpeg_jniGetWidth +Java_jpegkit_Jpeg_jniGetWidth (JNIEnv *env, jobject obj, jobject handle) { Jpeg *jpeg = (Jpeg *) env->GetDirectBufferAddress(handle); @@ -132,7 +132,7 @@ Java_com_jpegkit_Jpeg_jniGetWidth } JNIEXPORT jint JNICALL -Java_com_jpegkit_Jpeg_jniGetHeight +Java_jpegkit_Jpeg_jniGetHeight (JNIEnv *env, jobject obj, jobject handle) { Jpeg *jpeg = (Jpeg *) env->GetDirectBufferAddress(handle); @@ -147,7 +147,7 @@ Java_com_jpegkit_Jpeg_jniGetHeight } JNIEXPORT void JNICALL -Java_com_jpegkit_Jpeg_jniRotate +Java_jpegkit_Jpeg_jniRotate (JNIEnv *env, jobject obj, jobject handle, jint degrees) { Jpeg *jpeg = (Jpeg *) env->GetDirectBufferAddress(handle); @@ -164,7 +164,7 @@ Java_com_jpegkit_Jpeg_jniRotate } JNIEXPORT void JNICALL -Java_com_jpegkit_Jpeg_jniFlipHorizontal +Java_jpegkit_Jpeg_jniFlipHorizontal (JNIEnv *env, jobject obj, jobject handle) { Jpeg *jpeg = (Jpeg *) env->GetDirectBufferAddress(handle); @@ -175,7 +175,7 @@ Java_com_jpegkit_Jpeg_jniFlipHorizontal } JNIEXPORT void JNICALL -Java_com_jpegkit_Jpeg_jniFlipVertical +Java_jpegkit_Jpeg_jniFlipVertical (JNIEnv *env, jobject obj, jobject handle) { Jpeg *jpeg = (Jpeg *) env->GetDirectBufferAddress(handle); @@ -186,7 +186,7 @@ Java_com_jpegkit_Jpeg_jniFlipVertical } JNIEXPORT void JNICALL -Java_com_jpegkit_Jpeg_jniCrop +Java_jpegkit_Jpeg_jniCrop (JNIEnv *env, jobject obj, jobject handle, jint left, jint top, jint width, jint height) { Jpeg *jpeg = (Jpeg *) env->GetDirectBufferAddress(handle); @@ -201,4 +201,4 @@ Java_com_jpegkit_Jpeg_jniCrop transform->options = TJXOPT_CROP; tjTransform(jpeg->transformer, jpeg->buffer, jpeg->size, 1, &jpeg->buffer, &jpeg->size, transform, 0); -} \ No newline at end of file +} diff --git a/jpegkit/src/main/cpp/jpegkit/render.cpp b/jpegkit/src/main/cpp/jpegkit/render.cpp new file mode 100644 index 0000000..2a470f2 --- /dev/null +++ b/jpegkit/src/main/cpp/jpegkit/render.cpp @@ -0,0 +1,21 @@ +#include "platform.h" +#include +#include +#include +#include +#include + +extern "C" JNIEXPORT void JNICALL +Java_com_jpegkit_JpegView_renderJpeg(JNIEnv *env, jobject obj, jobject surface, jlong allocHandle, jlong jpegSize, jint width, jint height) { + ANativeWindow *window = ANativeWindow_fromSurface(env, surface); + ANativeWindow_setBuffersGeometry(window, width, height, WINDOW_FORMAT_RGBA_8888); + ANativeWindow_Buffer windowBuffer; + if (ANativeWindow_lock(window, &windowBuffer, NULL) == 0) { + Allocation *alloc = (Allocation *) allocHandle; + unsigned char *jpegBuf = alloc->bytes; + memcpy(windowBuffer.bits, jpegBuf, (size_t) jpegSize); + ANativeWindow_unlockAndPost(window); + } +} + + diff --git a/jpegkit/src/main/cpp/jpegkit/tjwrapper.cpp b/jpegkit/src/main/cpp/jpegkit/tjwrapper.cpp new file mode 100644 index 0000000..5e0a972 --- /dev/null +++ b/jpegkit/src/main/cpp/jpegkit/tjwrapper.cpp @@ -0,0 +1,332 @@ +#include "platform.h" + +typedef struct { tjhandle handle; } Command; + +static tjtransform *getTransform(JNIEnv *env, jobject jTransform) { + tjtransform *transform = new tjtransform(); + + jclass jTransformCls = env->FindClass("libjpeg/TurboJpeg$Transform"); + + jfieldID opFieldID = env->GetFieldID(jTransformCls, "op", "I"); + jfieldID optionsFieldID = env->GetFieldID(jTransformCls, "options", "I"); + + transform->op = env->GetIntField(jTransform, opFieldID); + transform->options = env->GetIntField(jTransform, optionsFieldID); + + jfieldID rFieldID = env->GetFieldID(jTransformCls, "r", "Llibjpeg/TurboJpeg$Transform$Region;"); + jobject jRegion = env->GetObjectField(jTransform, rFieldID); + + if (jRegion) { + tjregion r; + + jclass jRegionCls = env->FindClass("libjpeg/TurboJpeg$Transform$Region"); + jfieldID rxFieldID = env->GetFieldID(jRegionCls, "x", "I"); + jfieldID ryFieldID = env->GetFieldID(jRegionCls, "y", "I"); + jfieldID rwFieldID = env->GetFieldID(jRegionCls, "w", "I"); + jfieldID rhFieldID = env->GetFieldID(jRegionCls, "h", "I"); + + r.x = env->GetIntField(jRegion, rxFieldID); + r.y = env->GetIntField(jRegion, ryFieldID); + r.w = env->GetIntField(jRegion, rwFieldID); + r.h = env->GetIntField(jRegion, rhFieldID); + + transform->r = r; + } + + return transform; +} + +extern "C" JNIEXPORT jint JNICALL +Java_libjpeg_TurboJpeg_TJPAD(JNIEnv *env, jclass clazz, jint width) { + return TJPAD(width); +} + +extern "C" JNIEXPORT jint JNICALL +Java_libjpeg_TurboJpeg_TJSCALED(JNIEnv *env, jclass clazz, jint dimension, jintArray scalefactor) { + jint scalefactorElements[2]; + env->GetIntArrayRegion(scalefactor, 0, 2, scalefactorElements); + tjscalingfactor factor = {scalefactorElements[0], scalefactorElements[1]}; + return TJSCALED(dimension, factor); +} + +extern "C" JNIEXPORT jlong JNICALL +Java_libjpeg_TurboJpeg_tjInitCompress(JNIEnv *env, jclass clazz) { + Command *command = new Command(); + command->handle = tjInitCompress(); + return (long) command; +} + +extern "C" JNIEXPORT jint JNICALL +Java_libjpeg_TurboJpeg_tjCompress2(JNIEnv *env, jclass clazz, jlong cmdHandle, jlong srcHandle, + jint width, jint pitch, jint height, jint pixelFormat, + jlong dstHandle, jlongArray jpegSizeDst, jint jpegSubsamp, + jint jpegQual, jint flags) { + Command *command = (Command *) cmdHandle; + + Allocation *srcAlloc = (Allocation *) srcHandle; + unsigned char *srcBuf = srcAlloc->bytes; + + Allocation *dstAlloc = (Allocation *) dstHandle; + unsigned char *dstBuf = dstAlloc->bytes; + + unsigned long jpegSize; + + int status = tjCompress2( + command->handle, + srcBuf, + width, + pitch, + height, + pixelFormat, + &dstBuf, + &jpegSize, + jpegSubsamp, + jpegQual, + flags + ); + + jlong jpegSizeDstElements[1]; + jpegSizeDstElements[0] = jpegSize; + + env->SetLongArrayRegion(jpegSizeDst, 0, 1, jpegSizeDstElements); + + return status; +} + +extern "C" JNIEXPORT jlong JNICALL +Java_libjpeg_TurboJpeg_tjBufSize(JNIEnv *env, jclass clazz, jint width, jint height, jint jpegSubsamp) { + return tjBufSize(width, height, jpegSubsamp); +} + +extern "C" JNIEXPORT jlong JNICALL +Java_libjpeg_TurboJpeg_tjBufSizeYUV(JNIEnv *env, jclass clazz, jint width, jint height, jint subsamp) { + return tjBufSizeYUV(width, height, subsamp); +} + +extern "C" JNIEXPORT jint JNICALL +Java_libjpeg_TurboJpeg_tjEncodeYUV2(JNIEnv *env, jclass clazz, jlong cmdHandle, jlong srcHandle, + jint width, jint pitch, jint height, jint pixelFormat, + jlong dstHandle, jint subsamp, jint flags) { + Command *command = (Command *) cmdHandle; + + Allocation *srcAlloc = (Allocation *) srcHandle; + unsigned char *srcBuf = srcAlloc->bytes; + + Allocation *dstAlloc = (Allocation *) dstHandle; + unsigned char *dstBuf = dstAlloc->bytes; + + int status = tjEncodeYUV2( + command->handle, + srcBuf, + width, + pitch, + height, + pixelFormat, + dstBuf, + subsamp, + flags + ); + + return status; +} + +extern "C" JNIEXPORT jlong JNICALL +Java_libjpeg_TurboJpeg_tjInitDecompress(JNIEnv *env, jclass clazz) { + Command *command = new Command(); + command->handle = tjInitDecompress(); + return (long) command; +} + +extern "C" JNIEXPORT jint JNICALL +Java_libjpeg_TurboJpeg_tjDecompressHeader2(JNIEnv *env, jclass clazz, jlong cmdHandle, + jlong srcHandle, jlong jpegSize, jintArray outputs) { + Command *command = (Command *) cmdHandle; + Allocation *srcAlloc = (Allocation *) srcHandle; + unsigned char *srcBuf = srcAlloc->bytes; + + int width; + int height; + int jpegSubsamp; + + int status = tjDecompressHeader2( + command->handle, + srcBuf, + (unsigned long) jpegSize, + &width, + &height, + &jpegSubsamp + ); + + jint outputsElements[3]; + outputsElements[0] = (jint) width; + outputsElements[1] = (jint) height; + outputsElements[2] = (jint) jpegSubsamp; + + env->SetIntArrayRegion(outputs, 0, 3, outputsElements); + + return status; +} + +extern "C" JNIEXPORT jobjectArray JNICALL +Java_libjpeg_TurboJpeg_tjGetScalingFactors(JNIEnv *env, jclass clazz) { + int numscalingfactors; + tjscalingfactor *scalingfactors = tjGetScalingFactors(&numscalingfactors); + + jclass intArrayCls = env->FindClass("[I"); + jobjectArray outputs = env->NewObjectArray(numscalingfactors, intArrayCls, NULL); + + for (int i = 0; i < numscalingfactors; i++) { + tjscalingfactor scalingfactor = scalingfactors[i]; + jintArray scalingfactorOutputs = env->NewIntArray(2); + + int scalingfactorValues[] = {scalingfactor.num, scalingfactor.denom}; + env->SetIntArrayRegion(scalingfactorOutputs, 0, 2, scalingfactorValues); + env->SetObjectArrayElement(outputs, i, scalingfactorOutputs); + } + + return outputs; +} + +extern "C" JNIEXPORT jint JNICALL +Java_libjpeg_TurboJpeg_tjDecompress2(JNIEnv *env, jclass clazz, jlong cmdHandle, jlong srcHandle, + jlong jpegSize, jlong dstHandle, jint width, jint pitch, + jint height, jint pixelFormat, jint flags) { + Command *command = (Command *) cmdHandle; + + Allocation *srcAlloc = (Allocation *) srcHandle; + unsigned char *srcBuf = srcAlloc->bytes; + + Allocation *dstAlloc = (Allocation *) dstHandle; + unsigned char *dstBuf = dstAlloc->bytes; + + int status = tjDecompress2( + command->handle, + srcBuf, + (unsigned long) jpegSize, + dstBuf, + width, + pitch, + height, + pixelFormat, + flags + ); + + return status; +} + +extern "C" JNIEXPORT jint JNICALL +Java_libjpeg_TurboJpeg_tjDecompressToYUV(JNIEnv *env, jclass clazz, jlong cmdHandle, jlong srcHandle, + jlong jpegSize, jlong dstHandle, jint flags) { + Command *command = (Command *) cmdHandle; + + Allocation *srcAlloc = (Allocation *) srcHandle; + unsigned char *srcBuf = srcAlloc->bytes; + + Allocation *dstAlloc = (Allocation *) dstHandle; + unsigned char *dstBuf = dstAlloc->bytes; + + int status = tjDecompressToYUV( + command->handle, + srcBuf, + (unsigned long) jpegSize, + dstBuf, + flags + ); + + return status; +} + +extern "C" JNIEXPORT jlong JNICALL +Java_libjpeg_TurboJpeg_tjInitTransform(JNIEnv *env, jclass clazz) { + Command *command = new Command(); + command->handle = tjInitTransform(); + return (long) command; +} + +extern "C" JNIEXPORT jint JNICALL +Java_libjpeg_TurboJpeg_tjTransform(JNIEnv *env, jclass clazz, jlong cmdHandle, jlong srcHandle, + jlong jpegSize, jint n, jlongArray dstHandles, + jlongArray sizeOutputs, jobjectArray jTransforms, jint flags) { + Command *command = (Command *) cmdHandle; + + Allocation *srcAlloc = (Allocation *) srcHandle; + unsigned char *srcBuf = srcAlloc->bytes; + + unsigned char *dstBufs[n]; + jlong dstHandlesElements[n]; + + env->GetLongArrayRegion(dstHandles, 0, n, dstHandlesElements); + + for (int i = 0; i < n; i++) { + Allocation *dstAlloc = (Allocation *) dstHandlesElements[i]; + dstBufs[i] = dstAlloc->bytes; + } + + unsigned long dstSizes[n]; + + int numTransforms = env->GetArrayLength(jTransforms); + tjtransform transforms[numTransforms]; + for (int i = 0; i < numTransforms; i++) { + transforms[i] = *getTransform(env, env->GetObjectArrayElement(jTransforms, i)); + } + + int status = tjTransform( + command->handle, + srcBuf, + (unsigned long) jpegSize, + n, + dstBufs, + dstSizes, + transforms, + flags + ); + + jlong sizeOutputsElements[n]; + for (int i = 0; i < n; i++) sizeOutputsElements[i] = dstSizes[i]; + env->SetLongArrayRegion(sizeOutputs, 0, n, sizeOutputsElements); + + return status; +} + +extern "C" JNIEXPORT jint JNICALL +Java_libjpeg_TurboJpeg_tjDestroy(JNIEnv *env, jclass clazz, jlong cmdHandle) { + Command *command = (Command *) cmdHandle; + int status = tjDestroy(command->handle); + delete command; + return status; +} + +extern "C" JNIEXPORT jlong JNICALL +Java_libjpeg_TurboJpeg_tjAlloc(JNIEnv *env, jclass clazz, jint size) { + Allocation *allocation = new Allocation(); + allocation->bytes = tjAlloc(size); + return (long) allocation; +} + +extern "C" JNIEXPORT void JNICALL +Java_libjpeg_TurboJpeg_tjFree(JNIEnv *env, jclass clazz, jlong allocHandle) { + Allocation *allocation = (Allocation *) allocHandle; + tjFree(allocation->bytes); + delete allocation; +} + +extern "C" JNIEXPORT jstring JNICALL +Java_libjpeg_TurboJpeg_tjGetErrorStr(JNIEnv *env, jclass clazz) { + return env->NewStringUTF(tjGetErrorStr()); +} + +extern "C" JNIEXPORT jlong JNICALL +Java_libjpeg_TurboJpeg_tjwSrcToAlloc(JNIEnv *env, jclass clazz, jlong allocHandle, jbyteArray src) { + Allocation *allocation = (Allocation *) allocHandle; + int srcLength = env->GetArrayLength(src); + allocation->bytes = tjAlloc(srcLength); + env->GetByteArrayRegion(src, 0, srcLength, reinterpret_cast(allocation->bytes)); + return (long) allocation; +} + +extern "C" JNIEXPORT void JNICALL +Java_libjpeg_TurboJpeg_tjwAllocToDst(JNIEnv *env, jclass clazz, jlong allocHandle, jbyteArray dst) { + Allocation *allocation = (Allocation *) allocHandle; + int dstLength = env->GetArrayLength(dst); + env->SetByteArrayRegion(dst, 0, dstLength, reinterpret_cast(allocation->bytes)); +} diff --git a/jpegkit/src/main/java/com/jpegkit/Jpeg.java b/jpegkit/src/main/java/jpegkit/Jpeg.java similarity index 99% rename from jpegkit/src/main/java/com/jpegkit/Jpeg.java rename to jpegkit/src/main/java/jpegkit/Jpeg.java index 662e2b2..6a0b81c 100644 --- a/jpegkit/src/main/java/com/jpegkit/Jpeg.java +++ b/jpegkit/src/main/java/jpegkit/Jpeg.java @@ -1,4 +1,4 @@ -package com.jpegkit; +package jpegkit; import android.graphics.Rect; import android.os.Parcel; @@ -7,6 +7,7 @@ import java.nio.ByteBuffer; +@Deprecated public class Jpeg implements Parcelable { private static final Object sJniLock = new Object(); diff --git a/jpegkit/src/main/java/jpegkit/JpegDecoder.java b/jpegkit/src/main/java/jpegkit/JpegDecoder.java new file mode 100644 index 0000000..85f7bb4 --- /dev/null +++ b/jpegkit/src/main/java/jpegkit/JpegDecoder.java @@ -0,0 +1,183 @@ +package jpegkit; + +import android.support.annotation.NonNull; + +import static libjpeg.TurboJpeg.*; + +public class JpegDecoder extends JpegHandler { + + private long allocHandle; + private int jpegSize; + + private int width; + private int height; + private int subsampling; + + private JpegDecoder() { + throw new RuntimeException("No empty constructor allowed."); + } + + public JpegDecoder(@NonNull byte[] jpeg) throws JpegKitException { + // In constructor command success is required for continued use of this JpegDecoder, + // so we need to catch any CommandException to invalidate the object's state fields + // before re-throwing. + try { + allocHandle = tjAlloc(jpeg.length); + checkCommandError(); + tjwSrcToAlloc(allocHandle, jpeg); + checkCommandError(); + + jpegSize = jpeg.length; + + long decompressHandle = tjInitDecompress(); + checkCommandError(); + + int[] outputs = new int[3]; + tjDecompressHeader2(decompressHandle, allocHandle, jpegSize, outputs); + checkCommandError(); + + tjDestroy(decompressHandle); + checkCommandError(); + + width = outputs[0]; + height = outputs[1]; + subsampling = outputs[2]; + } catch (CommandException e) { + if (allocHandle != 0) { + // Try to free the allocation just in case the current state + // makes that necessary and possible. + tjFree(allocHandle); + } + + allocHandle = -1; + throw e; + } + } + + @Override + protected void checkStateError() throws StateException { + if (allocHandle == -1) { + throw new StateException("Invalid reference to JNI allocation for jpeg data."); + } + } + + public int getWidth() { + return width; + } + + public int getHeight() { + return height; + } + + @Subsampling + public int getSubsampling() { + return subsampling; + } + + public byte[] decode() throws JpegKitException { + return decode(0, 0, 0, PIXEL_FORMAT_RGB /* 0 */, 0); + } + + public byte[] decode(int width, int height, int pitch, @PixelFormat int pixelFormat, @Flag int flags) throws JpegKitException { + checkStateError(); + + if (width == 0) { + width = this.width; + } + + if (height == 0) { + height = this.height; + } + + if (pitch == 0) { + pitch = computePitch(width, null, pixelFormat, false); + } + + long decompressHandle = tjInitDecompress(); + checkCommandError(); + + int dstSize = pitch * TJSCALED(height, new int[]{1, 1}); + + long dstAllocHandle = tjAlloc(dstSize); + checkCommandError(); + + tjDecompress2(decompressHandle, allocHandle, jpegSize, dstAllocHandle, width, pitch, height, pixelFormat, flags); + checkCommandError(); + + tjDestroy(decompressHandle); + checkCommandError(); + + byte[] decoded = new byte[dstSize]; + tjwAllocToDst(dstAllocHandle, decoded); + checkCommandError(); + + tjFree(dstAllocHandle); + checkCommandError(); + + return decoded; + } + + public byte[] decodeToYuv() throws JpegKitException { + return decodeToYuv(0); + } + + public byte[] decodeToYuv(@Flag int flags) throws JpegKitException { + checkStateError(); + + long decompressHandle = tjInitDecompress(); + checkCommandError(); + + int dstSize = (int) tjBufSizeYUV(width, height, subsampling); + + long dstAllocHandle = tjAlloc(dstSize); + checkCommandError(); + + tjDecompressToYUV(decompressHandle, allocHandle, jpegSize, dstAllocHandle, flags); + checkCommandError(); + + tjDestroy(decompressHandle); + checkCommandError(); + + byte[] decoded = new byte[dstSize]; + tjwAllocToDst(dstAllocHandle, decoded); + checkCommandError(); + + tjFree(dstAllocHandle); + checkCommandError(); + + return decoded; + } + + public void release() throws JpegKitException { + checkStateError(); + + tjFree(allocHandle); + allocHandle = -1; + checkCommandError(); + } + + public byte[] decodeAndRelease(int width, int height, int pitch, @PixelFormat int pixelFormat, @Flag int flags) throws JpegKitException { + byte[] output = decode(width, height, pitch, pixelFormat, flags); + release(); + return output; + } + + public byte[] decodeAndRelease() throws JpegKitException { + byte[] output = decode(); + release(); + return output; + } + + public byte[] decodeToYuvAndRelease(@Flag int flags) throws JpegKitException { + byte[] output = decodeToYuv(flags); + release(); + return output; + } + + public byte[] decodeToYuvAndRelease() throws JpegKitException { + byte[] output = decodeToYuv(); + release(); + return output; + } + +} diff --git a/jpegkit/src/main/java/jpegkit/JpegEncoder.java b/jpegkit/src/main/java/jpegkit/JpegEncoder.java new file mode 100644 index 0000000..57a7425 --- /dev/null +++ b/jpegkit/src/main/java/jpegkit/JpegEncoder.java @@ -0,0 +1,4 @@ +package jpegkit; + +public class JpegEncoder { +} diff --git a/jpegkit/src/main/java/com/jpegkit/JpegFile.java b/jpegkit/src/main/java/jpegkit/JpegFile.java similarity index 98% rename from jpegkit/src/main/java/com/jpegkit/JpegFile.java rename to jpegkit/src/main/java/jpegkit/JpegFile.java index fde80bb..03261cc 100644 --- a/jpegkit/src/main/java/com/jpegkit/JpegFile.java +++ b/jpegkit/src/main/java/jpegkit/JpegFile.java @@ -1,4 +1,4 @@ -package com.jpegkit; +package jpegkit; import android.os.Parcel; import android.os.Parcelable; @@ -9,6 +9,7 @@ import java.io.FileOutputStream; import java.io.IOException; +@Deprecated public class JpegFile extends Jpeg { private File mJpegFile; @@ -39,7 +40,6 @@ private static byte[] dumpFile(File file) throws IOException { byte[] fileBytes = new byte[inputStream.available()]; inputStream.read(fileBytes); inputStream.close(); - return fileBytes; } diff --git a/jpegkit/src/main/java/jpegkit/JpegHandler.java b/jpegkit/src/main/java/jpegkit/JpegHandler.java new file mode 100644 index 0000000..cd0d123 --- /dev/null +++ b/jpegkit/src/main/java/jpegkit/JpegHandler.java @@ -0,0 +1,105 @@ +package jpegkit; + +import android.support.annotation.IntDef; +import android.support.annotation.Nullable; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +import static libjpeg.TurboJpeg.*; + +abstract class JpegHandler { + + public static final int SUBSAMPLING_444 = TJSAMP_444; + public static final int SUBSAMPLING_422 = TJSAMP_422; + public static final int SUBSAMPLING_420 = TJSAMP_420; + public static final int SUBSAMPLING_GRAY = TJSAMP_GRAY; + public static final int SUBSAMPLING_440 = TJSAMP_440; + + @Retention(RetentionPolicy.SOURCE) + @IntDef({SUBSAMPLING_444, SUBSAMPLING_422, SUBSAMPLING_420, SUBSAMPLING_GRAY, SUBSAMPLING_440}) + public @interface Subsampling { + } + + public static final int PIXEL_FORMAT_RGB = TJPF_RGB; + public static final int PIXEL_FORMAT_BGR = TJPF_BGR; + public static final int PIXEL_FORMAT_RGBX = TJPF_RGBX; + public static final int PIXEL_FORMAT_BGRX = TJPF_BGRX; + public static final int PIXEL_FORMAT_XBGR = TJPF_XBGR; + public static final int PIXEL_FORMAT_XRGB = TJPF_XRGB; + public static final int PIXEL_FORMAT_GRAY = TJPF_GRAY; + public static final int PIXEL_FORMAT_RGBA = TJPF_RGBA; + public static final int PIXEL_FORMAT_BGRA = TJPF_BGRA; + public static final int PIXEL_FORMAT_ABGR = TJPF_ABGR; + public static final int PIXEL_FORMAT_ARGB = TJPF_ARGB; + + @Retention(RetentionPolicy.SOURCE) + @IntDef({PIXEL_FORMAT_RGB, PIXEL_FORMAT_BGR, PIXEL_FORMAT_RGBX, PIXEL_FORMAT_BGRX, + PIXEL_FORMAT_XBGR, PIXEL_FORMAT_XRGB, PIXEL_FORMAT_GRAY, PIXEL_FORMAT_RGBA, + PIXEL_FORMAT_BGRA, PIXEL_FORMAT_ABGR, PIXEL_FORMAT_ARGB}) + public @interface PixelFormat { + } + + public static final int FLAG_NONE = 0; + public static final int FLAG_BOTTOM_UP = TJFLAG_BOTTOMUP; + public static final int FLAG_FORCE_MMX = TJFLAG_FORCEMMX; + public static final int FLAG_FORCE_SSE = TJFLAG_FORCESSE; + public static final int FLAG_FORCE_SSE2 = TJFLAG_FORCESSE2; + public static final int FLAG_FORCE_SSE3 = TJFLAG_FORCESSE3; + public static final int FLAG_FAST_UPSAMPLE = TJFLAG_FASTUPSAMPLE; + public static final int FLAG_NO_REALLOC = TJFLAG_NOREALLOC; + + @Retention(RetentionPolicy.SOURCE) + @IntDef(flag = true, value = {FLAG_NONE, FLAG_BOTTOM_UP, FLAG_FORCE_MMX, FLAG_FORCE_SSE, + FLAG_FORCE_SSE2, FLAG_FORCE_SSE3, FLAG_FAST_UPSAMPLE, FLAG_NO_REALLOC}) + public @interface Flag { + } + + private String errorString; + + JpegHandler() { + } + + protected abstract void checkStateError() throws StateException; + + protected void checkCommandError() throws CommandException { + String lastCommandErrorString = tjGetErrorStr(); + if (lastCommandErrorString != null && lastCommandErrorString.length() > 0 && !lastCommandErrorString.equals("No error")) { + errorString = lastCommandErrorString; + throw new CommandException(errorString); + } + } + + public int computePitch(int width, @Nullable int[] scalefactor, @PixelFormat int pixelFormat, boolean isPaddedTo32BitBoundary) { + if (scalefactor == null) { + scalefactor = new int[]{1, 1}; + } + + if (isPaddedTo32BitBoundary) { + return TJPAD(TJSCALED(width, scalefactor) * tjPixelSize[pixelFormat]); + } else { + return TJSCALED(width, scalefactor) * tjPixelSize[pixelFormat]; + } + } + + public static class CommandException extends JpegKitException { + CommandException(String message) { + super(message); + } + + CommandException(String message, Throwable cause) { + super(message, cause); + } + } + + public static class StateException extends JpegKitException { + StateException(String s) { + super(s); + } + + StateException(String message, Throwable cause) { + super(message, cause); + } + } + +} diff --git a/jpegkit/src/main/java/com/jpegkit/JpegImageView.java b/jpegkit/src/main/java/jpegkit/JpegImageView.java similarity index 98% rename from jpegkit/src/main/java/com/jpegkit/JpegImageView.java rename to jpegkit/src/main/java/jpegkit/JpegImageView.java index 0f096d2..5124532 100644 --- a/jpegkit/src/main/java/com/jpegkit/JpegImageView.java +++ b/jpegkit/src/main/java/jpegkit/JpegImageView.java @@ -1,4 +1,4 @@ -package com.jpegkit; +package jpegkit; import android.annotation.TargetApi; import android.content.Context; @@ -8,6 +8,7 @@ import android.util.AttributeSet; import android.widget.ImageView; +@Deprecated public class JpegImageView extends ImageView { private Jpeg mJpeg; diff --git a/jpegkit/src/main/java/com/jpegkit/JpegKit.java b/jpegkit/src/main/java/jpegkit/JpegKit.java similarity index 99% rename from jpegkit/src/main/java/com/jpegkit/JpegKit.java rename to jpegkit/src/main/java/jpegkit/JpegKit.java index 7ca7771..391315f 100644 --- a/jpegkit/src/main/java/com/jpegkit/JpegKit.java +++ b/jpegkit/src/main/java/jpegkit/JpegKit.java @@ -1,4 +1,4 @@ -package com.jpegkit; +package jpegkit; import android.content.Context; import android.os.Environment; @@ -9,6 +9,7 @@ import java.io.FileOutputStream; import java.io.IOException; +@Deprecated public final class JpegKit { private static void writeFile(Jpeg jpeg, File file) throws IOException { diff --git a/jpegkit/src/main/java/jpegkit/JpegKitException.java b/jpegkit/src/main/java/jpegkit/JpegKitException.java new file mode 100644 index 0000000..82fb0fa --- /dev/null +++ b/jpegkit/src/main/java/jpegkit/JpegKitException.java @@ -0,0 +1,17 @@ +package jpegkit; + +public class JpegKitException extends Exception { + + private JpegKitException() { + super(); + } + + JpegKitException(String message) { + super(message); + } + + JpegKitException(String message, Throwable cause) { + super(message, cause); + } + +} diff --git a/jpegkit/src/main/java/jpegkit/JpegTransformer.java b/jpegkit/src/main/java/jpegkit/JpegTransformer.java new file mode 100644 index 0000000..7d7b1f3 --- /dev/null +++ b/jpegkit/src/main/java/jpegkit/JpegTransformer.java @@ -0,0 +1,191 @@ +package jpegkit; + +import android.support.annotation.NonNull; + +import static libjpeg.TurboJpeg.*; + +public class JpegTransformer extends JpegHandler { + + private long allocHandle; + private int jpegSize; + + private int width; + private int height; + private int jpegSubsamp; + + private JpegTransformer() { + super(); + throw new RuntimeException("No empty constructor allowed."); + } + + public JpegTransformer(@NonNull byte[] jpeg) throws JpegKitException { + super(); + + // In constructor command success is required for continued use of this JpegTransformer, + // so we need to catch any CommandException to invalidate the object's state fields + // before re-throwing. + try { + allocHandle = tjAlloc(jpeg.length); + checkCommandError(); + tjwSrcToAlloc(allocHandle, jpeg); + checkCommandError(); + + jpegSize = jpeg.length; + invalidateMetadata(); + } catch (CommandException e) { + if (allocHandle != 0) { + // Try to free the allocation just in case the current state + // makes that necessary and possible. + tjFree(allocHandle); + } + + allocHandle = -1; + throw e; + } + } + + @Override + protected void checkStateError() throws StateException { + if (allocHandle == -1) { + throw new StateException("Invalid reference to JNI allocation for jpeg data."); + } + } + + private void invalidateMetadata() throws CommandException { + long decompressHandle = tjInitDecompress(); + checkCommandError(); + + int[] outputs = new int[3]; + tjDecompressHeader2(decompressHandle, allocHandle, jpegSize, outputs); + checkCommandError(); + + tjDestroy(decompressHandle); + checkCommandError(); + + width = outputs[0]; + height = outputs[1]; + jpegSubsamp = outputs[2]; + } + + private void transform(Transform transform) throws CommandException { + long transformHandle = tjInitTransform(); + checkCommandError(); + + long maxDstSize = tjBufSize(width, height, jpegSubsamp); + checkCommandError(); + + long dstAllocHandle = tjAlloc((int) maxDstSize); + checkCommandError(); + + long[] dstSizeOutput = new long[1]; + + tjTransform(transformHandle, allocHandle, jpegSize, 1, new long[]{dstAllocHandle}, + dstSizeOutput, new Transform[]{transform}, 0); + checkCommandError(); + + tjDestroy(transformHandle); + checkCommandError(); + + tjFree(allocHandle); + checkCommandError(); + + allocHandle = dstAllocHandle; + jpegSize = (int) dstSizeOutput[0]; + + invalidateMetadata(); + } + + public void flipHorizontal() throws JpegKitException { + checkStateError(); + + Transform transform = new Transform(); + transform.op = TJXOP_HFLIP; + transform(transform); + } + + public void flipVertical() throws JpegKitException { + checkStateError(); + + Transform transform = new Transform(); + transform.op = TJXOP_VFLIP; + transform(transform); + } + + public void rotate(int degrees) throws JpegKitException { + checkStateError(); + + Transform transform = new Transform(); + switch (degrees) { + case 1: + case 90: + case -3: + case -270: + transform.op = TJXOP_ROT90; + break; + + case 2: + case 180: + case -2: + case -180: + transform.op = TJXOP_ROT180; + break; + + case 3: + case 270: + case -1: + case -90: + transform.op = TJXOP_ROT270; + break; + + case 0: + default: + break; + } + + transform(transform); + } + + public void cropWithOffsets(int leftOffset, int topOffset, int rightOffset, int bottomOffset) throws JpegKitException { + leftOffset = leftOffset - (leftOffset % tjMCUWidth[jpegSubsamp]); + topOffset = topOffset - (topOffset % tjMCUHeight[jpegSubsamp]); + + cropWithRegion( + leftOffset, + topOffset, + width - leftOffset - rightOffset, + height - topOffset - bottomOffset + ); + } + + public void cropWithRegion(int x, int y, int width, int height) throws JpegKitException { + Transform transform = new Transform(); + transform.options = TJXOPT_CROP; + transform.r = new Transform.Region(x, y, width, height); + transform(transform); + } + + public byte[] obtain() throws JpegKitException { + checkStateError(); + + byte[] dst = new byte[jpegSize]; + tjwAllocToDst(allocHandle, dst); + checkCommandError(); + + return dst; + } + + public void release() throws JpegKitException { + checkStateError(); + + tjFree(allocHandle); + allocHandle = -1; + checkCommandError(); + } + + public byte[] obtainAndRelease() throws JpegKitException { + byte[] output = obtain(); + release(); + return output; + } + +} diff --git a/jpegkit/src/main/java/jpegkit/JpegView.java b/jpegkit/src/main/java/jpegkit/JpegView.java new file mode 100644 index 0000000..5e6871f --- /dev/null +++ b/jpegkit/src/main/java/jpegkit/JpegView.java @@ -0,0 +1,72 @@ +package jpegkit; + +import android.content.Context; +import android.util.AttributeSet; +import android.view.Surface; +import android.view.SurfaceHolder; +import android.view.SurfaceView; + +public class JpegView extends SurfaceView implements SurfaceHolder.Callback { + + private PixelAllocation pixelAllocation; + + public JpegView(Context context) { + super(context); + init(); + } + + public JpegView(Context context, AttributeSet attrs) { + super(context, attrs); + init(); + } + + private void init() { + getHolder().addCallback(this); + } + + @Override + public void surfaceCreated(SurfaceHolder surfaceHolder) { + if (pixelAllocation != null) { + renderJpeg(surfaceHolder.getSurface(), + pixelAllocation.getAllocHandle(), + pixelAllocation.getAllocSize(), + pixelAllocation.getWidth(), + pixelAllocation.getHeight()); + } + } + + @Override + public void surfaceChanged(SurfaceHolder surfaceHolder, int format, int width, int height) { + if (pixelAllocation != null) { + renderJpeg(surfaceHolder.getSurface(), + pixelAllocation.getAllocHandle(), + pixelAllocation.getAllocSize(), + pixelAllocation.getWidth(), + pixelAllocation.getHeight()); + } + } + + @Override + public void surfaceDestroyed(SurfaceHolder surfaceHolder) { + } + + public void setJpeg(byte[] jpeg) throws JpegKitException { + try { + if (pixelAllocation != null) { + pixelAllocation.release(); + pixelAllocation = null; + } + } catch (JpegKitException e) { + pixelAllocation = null; + } + + pixelAllocation = new PixelAllocation(jpeg, PixelAllocation.PIXEL_FORMAT_RGBA); + } + + private native void renderJpeg(Surface surface, long allocHandle, long jpegSize, int width, int height); + + static { + System.loadLibrary("jpegkit"); + } + +} diff --git a/jpegkit/src/main/java/jpegkit/PixelAllocation.java b/jpegkit/src/main/java/jpegkit/PixelAllocation.java new file mode 100644 index 0000000..d7ea1c4 --- /dev/null +++ b/jpegkit/src/main/java/jpegkit/PixelAllocation.java @@ -0,0 +1,113 @@ +package jpegkit; + +import android.support.annotation.NonNull; + +import static libjpeg.TurboJpeg.*; + +public class PixelAllocation extends JpegHandler { + + private long allocHandle; + private int allocSize; + + @PixelFormat + private int pixelFormat; + + private int width; + private int height; + + private PixelAllocation() { + throw new RuntimeException("No empty constructor allowed."); + } + + public PixelAllocation(@NonNull byte[] jpeg) throws JpegKitException { + this(jpeg, PIXEL_FORMAT_RGB); + } + + public PixelAllocation(@NonNull byte[] jpeg, @PixelFormat int dstPixelFormat) throws JpegKitException { + this.pixelFormat = dstPixelFormat; + + // In constructor command success is required for continued use of this PixelAllocation, + // so we need to catch any CommandException to invalidate the object's state fields + // before re-throwing. + try { + long jpegAllocHandle = tjAlloc(jpeg.length); + checkCommandError(); + tjwSrcToAlloc(jpegAllocHandle, jpeg); + checkCommandError(); + + long jpegAllocSize = jpeg.length; + + setJpeg(jpegAllocHandle, jpegAllocSize); + } catch (CommandException e) { + if (allocHandle != 0) { + // Try to free the allocation just in case the current state + // makes that necessary and possible. + tjFree(allocHandle); + } + + allocHandle = -1; + throw e; + } + } + + @Override + protected void checkStateError() throws StateException { + if (allocHandle == -1) { + throw new StateException("Invalid reference to JNI allocation for jpeg data."); + } + } + + private void setJpeg(long jpegAllocHandle, long jpegAllocSize) throws JpegKitException { + checkStateError(); + + long decompressHandle = tjInitDecompress(); + checkCommandError(); + + int[] outputs = new int[3]; + tjDecompressHeader2(decompressHandle, jpegAllocHandle, jpegAllocSize, outputs); + checkCommandError(); + + width = TJSCALED(outputs[0], new int[]{1, 1}); + height = TJSCALED(outputs[1], new int[]{1, 1}); + + int pitch = computePitch(outputs[0], new int[]{1, 1}, pixelFormat, false); + allocSize = pitch * TJSCALED(outputs[1], new int[]{1, 1}); + + allocHandle = tjAlloc(allocSize); + checkCommandError(); + + tjDecompress2(decompressHandle, jpegAllocHandle, jpegAllocSize, allocHandle, width, pitch, height, pixelFormat, 0); + checkCommandError(); + + tjDestroy(decompressHandle); + checkCommandError(); + + tjFree(jpegAllocHandle); + checkCommandError(); + } + + public int getWidth() { + return width; + } + + public int getHeight() { + return height; + } + + public int getAllocSize() { + return allocSize; + } + + public long getAllocHandle() { + return allocHandle; + } + + public void release() throws JpegKitException { + checkStateError(); + + tjFree(allocHandle); + allocHandle = -1; + checkCommandError(); + } + +} diff --git a/jpegkit/src/main/java/libjpeg/TurboJpeg.kt b/jpegkit/src/main/java/libjpeg/TurboJpeg.kt new file mode 100644 index 0000000..708df3a --- /dev/null +++ b/jpegkit/src/main/java/libjpeg/TurboJpeg.kt @@ -0,0 +1,193 @@ +package libjpeg + +class TurboJpeg { + + companion object { + + const val TJ_SUBSAMP: Int = 5 + const val TJSAMP_444: Int = 0 + const val TJSAMP_422: Int = 1 + const val TJSAMP_420: Int = 2 + const val TJSAMP_GRAY: Int = 3 + const val TJSAMP_440: Int = 4 + + const val TJ_NUMPF: Int = 11 + const val TJPF_RGB: Int = 0 + const val TJPF_BGR: Int = 1 + const val TJPF_RGBX: Int = 2 + const val TJPF_BGRX: Int = 3 + const val TJPF_XBGR: Int = 4 + const val TJPF_XRGB: Int = 5 + const val TJPF_GRAY: Int = 6 + const val TJPF_RGBA: Int = 7 + const val TJPF_BGRA: Int = 8 + const val TJPF_ABGR: Int = 9 + const val TJPF_ARGB: Int = 10 + + const val TJFLAG_BOTTOMUP: Int = 2 + const val TJFLAG_FORCEMMX: Int = 8 + const val TJFLAG_FORCESSE: Int = 16 + const val TJFLAG_FORCESSE2: Int = 32 + const val TJFLAG_FORCESSE3: Int = 128 + const val TJFLAG_FASTUPSAMPLE: Int = 256 + const val TJFLAG_NOREALLOC: Int = 1024 + + const val TJ_NUMXOP: Int = 8 + const val TJXOP_NONE: Int = 0 + const val TJXOP_HFLIP: Int = 1 + const val TJXOP_VFLIP: Int = 2 + const val TJXOP_TRANSPOSE: Int = 3 + const val TJXOP_TRANSVERSE: Int = 4 + const val TJXOP_ROT90: Int = 5 + const val TJXOP_ROT180: Int = 6 + const val TJXOP_ROT270: Int = 7 + + const val TJXOPT_PERFECT: Int = 1 + const val TJXOPT_TRIM: Int = 2 + const val TJXOPT_CROP: Int = 4 + const val TJXOPT_GRAY: Int = 8 + const val TJXOPT_NOOUTPUT: Int = 16 + + @JvmField + val tjRedOffset: IntArray = intArrayOf(0, 2, 0, 2, 3, 1, 0, 0, 2, 3, 1) + + @JvmField + val tjGreenOffset: IntArray = intArrayOf(1, 1, 1, 1, 2, 2, 0, 1, 1, 2, 2) + + @JvmField + val tjBlueOffset: IntArray = intArrayOf(2, 0, 2, 0, 1, 3, 0, 2, 0, 1, 3) + + @JvmField + val tjPixelSize: IntArray = intArrayOf(3, 3, 4, 4, 4, 4, 1, 4, 4, 4, 4) + + @JvmField + val tjMCUWidth: IntArray = intArrayOf(8, 16, 16, 8, 8) + + @JvmField + val tjMCUHeight: IntArray = intArrayOf(8, 8, 16, 8, 16) + + @JvmStatic + external fun TJPAD(width: Int): Int + + @JvmStatic + external fun TJSCALED(dimension: Int, scalefactor: IntArray): Int + + @JvmStatic + external fun tjInitCompress(): Long + + @JvmStatic + external fun tjCompress2(cmdHandle: Long, + srcHandle: Long, + width: Int, + pitch: Int, + height: Int, + pixelFormat: Int, + dstHandle: Long, + jpegSizeDst: LongArray, + jpegSubsamp: Int, + jpegQual: Int, + flags: Int): Int + + @JvmStatic + external fun tjBufSize(width: Int, height: Int, jpegSubsamp: Int): Long + + @JvmStatic + external fun tjBufSizeYUV(width: Int, height: Int, subsamp: Int): Long + + @JvmStatic + external fun tjEncodeYUV2(cmdHandle: Long, + srcHandle: Long, + width: Int, + pitch: Int, + height: Int, + pixelFormat: Int, + dstHandle: Long, + subsamp: Int, + flags: Int): Int + + @JvmStatic + external fun tjInitDecompress(): Long + + @JvmStatic + external fun tjDecompressHeader2(cmdHandle: Long, + srcHandle: Long, + jpegSize: Long, + outputs: IntArray): Int + + @JvmStatic + external fun tjGetScalingFactors(): Array + + @JvmStatic + external fun tjDecompress2(cmdHandle: Long, + srcHandle: Long, + jpegSize: Long, + dstHandle: Long, + width: Int, + pitch: Int, + height: Int, + pixelFormat: Int, + flags: Int): Int + + @JvmStatic + external fun tjDecompressToYUV(cmdHandle: Long, + srcHandle: Long, + jpegSize: Long, + dstHandle: Long, + flags: Int): Int + + @JvmStatic + external fun tjInitTransform(): Long + + @JvmStatic + external fun tjTransform(cmdHandle: Long, + srcHandle: Long, + jpegSize: Long, + n: Int, + dstHandles: LongArray, + sizeOutputs: LongArray, + transforms: Array, + flags: Int): Int + + @JvmStatic + external fun tjDestroy(cmdHandle: Long): Int + + @JvmStatic + external fun tjAlloc(size: Int): Long + + @JvmStatic + external fun tjFree(allocHandle: Long) + + @JvmStatic + external fun tjGetErrorStr(): String? + + @JvmStatic + external fun tjwAllocToDst(allocHandle: Long, dst: ByteArray) + + @JvmStatic + external fun tjwSrcToAlloc(allocHandle: Long, src: ByteArray): Long + + init { + System.loadLibrary("jpegkit") + } + + } + + class Transform { + + @JvmField + var r: Region? = null + + @JvmField + var op: Int = 0 + + @JvmField + var options: Int = 0 + + class Region(@JvmField var x: Int = 0, + @JvmField var y: Int = 0, + @JvmField var w: Int = 0, + @JvmField var h: Int = 0) + + } + +} \ No newline at end of file