First, what I (want) to do: compress and reduce the number of shots (jpg). Suppose the original image is 1600w x 1200h. Now I want to have one compressed copy of 1600x1200 and another 800x600 and 400x300.
What I use: For this I use libJpegTurob. If LibJpegTurob has some problems, I'm trying to use android data.
Already tried: First, I used Java Wrapper ported from Tom Gall ( https://github.com/jberkel/libjpeg-turbo ).
Everything went pretty well (on nexus 4) until I start using pictures larger than 4 MB. What basically happened is the android throwing OutOfMemory exceptions. This happened when I used smaller snapshots (~ 1-2 MB), but compressed one by one.
It got even worse when he ran it on budget devices with lower memory, such as the nexus s. The problem caused by the low heap is what I think.
Well then, I thought, I have to do this in c. The memory problems seem to be resolved as I used images smaller than 3mb on a budget device. On bunch 4, I could even compress the image> 15mb.
This is an src image. 
But now ... the problem. First compressed image looks good 
but everyone else looks like this:
or that 
This happened as long as I save the selected pictures and compress them.
Now the code.
Scaling and compression occurred here
#include "_HelloJNI.h" #include <errno.h> #include <jni.h> #include <sys/time.h> #include <time.h> #include <android/log.h> #include <stdio.h> #include <stdlib.h> #include <math.h> #include <android/bitmap.h> #include <unistd.h> #include <setjmp.h> #include "jpeglib.h" #include "turbojpeg.h" #define LOG_TAG "DEBUG" #define LOGI(...) __android_log_print(ANDROID_LOG_INFO,LOG_TAG,__VA_ARGS__) #define LOGV(...) __android_log_print(ANDROID_LOG_VERBOSE,LOG_TAG,__VA_ARGS__) #define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG,LOG_TAG,__VA_ARGS__) #define LOGE(...) __android_log_print(ANDROID_LOG_ERROR,LOG_TAG,__VA_ARGS__) int IMAGE_COMPRESS_QUALITY = 80; typedef struct { int width; int height; }tSize; JNIEXPORT jint JNICALL Java_com_example_LibJpegTurboTest_NdkCall_nativeCompress (JNIEnv * env, jobject onj, jstring jniSrcImgPath, jstring jniDestDir, jstring jniDestImgName, jint jniSrcWidth, jint jniSrcHeight) { int pyramidRet = 0; tSize fileSize; fileSize.width = (int)jniSrcWidth; fileSize.height = (int)jniSrcHeight; const char* srcImgPath = (*env)->GetStringUTFChars(env, jniSrcImgPath, 0); const char* destDir = (*env)->GetStringUTFChars(env, jniDestDir, 0); const char* destFileName = (*env)->GetStringUTFChars(env, jniDestImgName, 0); pyramidRet = createPreviewPyramidUsingCustomScaling(srcImgPath, destDir, destFileName, fileSize, 4); return 0; } static tSize imageSizeForStep(int step, tSize *originalSize) { float factor = 1 / pow(2, step); return (tSize) { round(originalSize->width * factor), round(originalSize->height * factor) }; } int saveBitmapBufferImage(unsigned char *data, tSize *imageSize, char *destFileName, int quality) { int retValue = 1; int res = 0; unsigned long destinationJpegBufferSize = 0; tjhandle tjCompressHandle = NULL; unsigned char *destinationJpegBuffer = NULL; FILE *file = NULL; // jpgeg compress tjCompressHandle = tjInitCompress(); if(tjCompressHandle == NULL) { retValue = -1; goto cleanup; } res = tjCompress2(tjCompressHandle, data, imageSize->width, imageSize->width * tjPixelSize[TJPF_RGBX], imageSize->height, TJPF_RGBX, &destinationJpegBuffer, &destinationJpegBufferSize, 1, quality, TJFLAG_FASTUPSAMPLE); if(res < 0) { retValue = -1; goto cleanup; } file = fopen(destFileName, "wb"); if(file == NULL) { retValue = -1; goto cleanup; } long written = fwrite(destinationJpegBuffer, destinationJpegBufferSize, 1, file); retValue = (written == 1); cleanup: if(tjCompressHandle) { tjDestroy(tjCompressHandle); } if(destinationJpegBuffer) { tjFree(destinationJpegBuffer); } if(file) { fclose(file); } return retValue; } int createBitmapBufferFromFile(char *srcFileName, tSize imageDimensions, long *bytesPerRow, long *dataBufferSize, unsigned char **dataBuffer) { int retValue = 1; int res = 0; FILE *file = NULL; unsigned char* sourceJpegBuffer = NULL; long sourceJpegBufferSize = 0; tjhandle tjDecompressHandle = NULL; int fileWidth = 0, fileHeight = 0, jpegSubsamp = 0; unsigned char* temp = NULL; unsigned char* rotatedSourceJpegBuffer = NULL; tjhandle tjTransformHandle = NULL; file = fopen(srcFileName, "rb"); if (file == NULL) { retValue = -1; goto cleanup; } res = fseek(file, 0, SEEK_END); if(res < 0) { retValue = -1; goto cleanup; } sourceJpegBufferSize = ftell(file); if(sourceJpegBufferSize <= 0) { retValue = -1; goto cleanup; } sourceJpegBuffer = tjAlloc(sourceJpegBufferSize); if(sourceJpegBuffer == NULL) { retValue = -1; goto cleanup; } res = fseek(file, 0, SEEK_SET); if(res < 0) { retValue = -1; goto cleanup; } res = fread(sourceJpegBuffer, (long)sourceJpegBufferSize, 1, file); if(res != 1) { retValue = -1; goto cleanup; } tjDecompressHandle = tjInitDecompress(); if(tjDecompressHandle == NULL) { retValue = -1; goto cleanup; } // decompress header to get image dimensions res = tjDecompressHeader2(tjDecompressHandle, sourceJpegBuffer, sourceJpegBufferSize, &fileWidth, &fileHeight, &jpegSubsamp); if(res < 0) { retValue = -1; goto cleanup; } float destWidth = (float)imageDimensions.width; float destHeight = (float)imageDimensions.height; *bytesPerRow = destWidth * tjPixelSize[TJPF_RGBX]; // buffer for uncompressed image-data *dataBufferSize = *bytesPerRow * destHeight; temp = tjAlloc(*dataBufferSize); if(temp == NULL) { retValue = -1; goto cleanup; } res = tjDecompress2(tjDecompressHandle, sourceJpegBuffer, sourceJpegBufferSize, temp, destWidth, *bytesPerRow, destHeight, TJPF_RGBX, TJ_FASTUPSAMPLE); if(res < 0) { retValue = -1; goto cleanup; } *dataBuffer = temp; temp = NULL; cleanup: if(file) { fclose(file); } if(sourceJpegBuffer) { tjFree(sourceJpegBuffer); } if(tjDecompressHandle) { tjDestroy(tjDecompressHandle); } if(temp) { tjFree(temp); } return retValue; } int createPreviewPyramidUsingCustomScaling(char* srcImgPath, char* destDir, char* destFileName, tSize orginalImgSize, int maxStep) { int retValue = 1; int res = 1; int success = 0; int loopStep = 0; tSize previewSize; long bytesPerRow; long oldBytesPerRow = 0; unsigned char* sourceDataBuffer = NULL; long sourceDataBufferSize = 0; unsigned char* destinationDataBuffer = NULL; long destinationDataBufferSize = 0; unsigned char* buf1 = NULL; unsigned char* buf2 = NULL; long workBufSize = 0; void* sourceRow = NULL; void* targetRow = NULL; char* destFilePrefix = "sample_"; char* fooDestName; char* fooStrBuilder; tSize orginSizeTmp; orginSizeTmp.width = orginalImgSize.width; orginSizeTmp.height = orginalImgSize.height; previewSize = imageSizeForStep(1, &orginSizeTmp); long width = (long)previewSize.width; long height = (long)previewSize.height; int errorCode = 0; errorCode = createBitmapBufferFromFile(srcImgPath, previewSize, &bytesPerRow, &sourceDataBufferSize, &buf1); if(errorCode != 1) { retValue = errorCode; goto cleanup; } workBufSize = sourceDataBufferSize; buf2 = tjAlloc(workBufSize); if(buf2 == NULL) { retValue = -1; goto cleanup; } else { memset(buf2,0,workBufSize); } sourceDataBuffer = buf1; fooDestName = strcat(destDir, destFilePrefix); fooStrBuilder = strcat(fooDestName, "1_"); fooDestName = strcat(fooStrBuilder, destFileName); success = saveBitmapBufferImage(sourceDataBuffer, &previewSize, fooDestName, IMAGE_COMPRESS_QUALITY); if(success <= 0) { retValue = -1; goto cleanup; } cleanup: if(sourceDataBuffer) { tjFree(sourceDataBuffer); } if(destinationDataBuffer) { tjFree(destinationDataBuffer); } return retValue; }
The Java part to start compression.
private void invokeCompress(ArrayList<PictureItem> picturesToCompress) { if(picturesToCompress != null && picturesToCompress.size() > 0) { for(int i=0; i<picturesToCompress.size(); i++) { String srcPicturePath = picturesToCompress.get(i).getSrcImg(); String destDir = "/storage/emulated/0/1_TEST_FOLDER/"; String destFileName = getRandomString(4)+".jpg"; BitmapFactory.Options options = new BitmapFactory.Options(); options.inJustDecodeBounds = true; BitmapFactory.decodeFile(srcPicturePath, options); try { ndkCall.compress(srcPicturePath, destDir, destFileName, options.outWidth, options.outHeight); } catch(Exception e) { e.printStackTrace(); } } } }
What am I doing wrong???
Thanks a lot!
PS Sorry for the bad English!