D'abord, ce que je (veux) faire : compresser et mettre à l'échelle un lot d'images (jpg). Supposons que l'image originale ait ces dimensions 1600w x 1200h. Maintenant, je veux avoir une copie compressée de 1600x1200 et une autre de 800x600 et 400x300.
Ce que j'utilise : J'utilise la libJpegTurob pour y parvenir. Si la libJpegTurob a des problèmes, j'essaie d'utiliser les méthodes données par Android.
Déjà essayé : D'abord, j'ai utilisé le Wrapper Java porté par Tom Gall ( https://github.com/jberkel/libjpeg-turbo ).
Tout s'est bien passé (sur le Nexus 4) jusqu'à ce que je commence à utiliser des images de plus de 4 Mo. Ce qui s'est passé, c'est qu'Android a lancé des exceptions OutOfMemory. Cela s'est produit lorsque j'ai utilisé des images plus petites (~1-2mb) mais compressées les unes après les autres.
C'est devenu encore pire après l'avoir fait fonctionner sur des appareils à bas prix avec moins de mémoire comme le Nexus S. Le problème est dû à la faible quantité de mémoire, c'est ce que je pense.
Alors, je me suis dit que je devais le faire en C. Les problèmes de mémoire semblent résolus, tant que j'utilise des images de moins de 3mb sur un appareil budgétaire. Sur un Nexus 4, j'ai même pu compresser une image de plus de 15 Mo.
C'est la photo de la src.
Mais maintenant... le problème. La première image compressée semble bonne
mais tous les autres ressemblent à ceci ou ceci
Cela s'est produit tant que j'ai sélectionné des images et que je les ai compressées.
Maintenant le code.
C'est ici que la mise à l'échelle et la compression ont lieu
#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;
}
La partie Java pour commencer la 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();
}
}
}
}
Qu'est-ce que j'ai fait de mal ?
Merci beaucoup !
P.S. Désolé pour le mauvais anglais !