I am building an app on which I to deal with extra large images like of 10000X5000 pixels.The app crashes when I tried to display such images on ImageView. I can't go for bitmap sampling as I can't change the size of images.
A simple work is to select an image of 10000X5000 pixels and display it in an ImageView.
Currently, I am using simple code for this purpose and app is crashing on it.
Uri selectedImage = data.getData();
String[] filePathColumn = { MediaStore.Images.Media.DATA };
// Get the cursor
Cursor cursor = getContentResolver().query(selectedImage,
filePathColumn, null, null, null);
// Move to first row
cursor.moveToFirst();
int columnIndex = cursor.getColumnIndex(filePathColumn[0]);
imgDecodableString = cursor.getString(columnIndex);
cursor.close();
ImageView imgView = (ImageView) findViewById(R.id.imgView);
// Set the Image in ImageView after decoding the String
imgView.setImageBitmap(BitmapFactory
.decodeFile(imgDecodableString));
The Exception is of OutOfMemory
E/AndroidRuntime: FATAL EXCEPTION: main Process: com.example.star.largeiamgesdemo,
PID: 21155 java.lang.OutOfMemoryError: Failed to allocate a 200000012 byte allocation with 4194208 free bytes and 123MB until OOM
Something like MemoryMapFile
may do the trick but it's for files didn't get anything for images in FileChannel
UPDATE
I can't scale down images while displaying cause I have to edit them (i.e. user can write something over the images, or place other images as watermark over them)
Thanks
As you say:
I can't go for bitmap sampling as I can't change the size of images.
so, reduce image size or sample it before show, is not ok. I think below my be a solution.
Because Android's dalvik limit, there is a max heap size you can use. When exceed it, crash happen.
In order to bypass dalvik limit, you can use jni to apply for memory from native heap. You must use libjpeg
in jni. You can choose show image on SurfaceView, then by holder.getSurface()
to get a Surface Object, pass it to jni, then use libjpeg
lib to decode image and show it. Next is some example code, call showJPG()
function to display large image.
JNIEXPORT void JNICALL Java_com_example_photoprocessing_activity_SurfaceProcessingActivity_showJPG(
JNIEnv * env, jobject activity, jobject surface, jstring img) {
const char * imgChar;
jboolean * isCopy;
imgChar = env->GetStringUTFChars(img, 0);
ANativeWindow_Buffer nwBuffer;
LOGI("img path : %s ",imgChar);
LOGI("ANativeWindow_fromSurface ");
ANativeWindow * mANativeWindow = ANativeWindow_fromSurface(env, surface);
if (mANativeWindow == NULL) {
LOGE("ANativeWindow_fromSurface error");
return;
}
LOGI("ANativeWindow_lock ");
if (0 != ANativeWindow_lock(mANativeWindow, &nwBuffer, 0)) {
LOGE("ANativeWindow_lock error");
return;
}
read_jpeg_file_show(imgChar, nwBuffer);
if (nwBuffer.format == WINDOW_FORMAT_RGBA_8888) {
LOGI("nwBuffer->format == WINDOW_FORMAT_RGBA_8888 ");
}
LOGI("ANativeWindow_unlockAndPost ");
if (0 != ANativeWindow_unlockAndPost(mANativeWindow)) {
LOGE("ANativeWindow_unlockAndPost error");
return;
}
env->ReleaseStringUTFChars(img,imgChar);
ANativeWindow_release(mANativeWindow);
LOGI("ANativeWindow_release ");
return;
}
int read_jpeg_file_show(const char *input_filename,
ANativeWindow_Buffer& nwBuffer) {
struct jpeg_decompress_struct cinfo;
struct jpeg_error_mgr jerr;
FILE *input_file;
JSAMPARRAY buffer;
int row_width;
unsigned char *buffertmp;
cinfo.err = jpeg_std_error(&jerr);
if ((input_file = fopen(input_filename, "rb")) == NULL) {
fprintf(stderr, "can't open %s\n", input_filename);
LOGI("can't open jpg1");
return -1;
}
jpeg_create_decompress(&cinfo);
/* Specify data source for decompression */
jpeg_stdio_src(&cinfo, input_file);
/* Read file header, set default decompression parameters */
(void) jpeg_read_header(&cinfo, TRUE);
/* Start decompressor */
(void) jpeg_start_decompress(&cinfo);
row_width = cinfo.output_width * cinfo.output_components;
buffer = (*cinfo.mem->alloc_sarray)((j_common_ptr) &cinfo, JPOOL_IMAGE,
row_width, 1);
buffertmp = (unsigned char *) malloc(row_width);
memset(buffertmp, 0, row_width);
LOGI("malloc and memset");
/* Process data */
int get8h5 = 248, get8h6 = 252;
__uint16_t * line = (__uint16_t *) nwBuffer.bits;
int wheight = 0;
int scalew = 1, scaleh = 1;
if (cinfo.output_width > nwBuffer.width) {
scalew = cinfo.output_width / nwBuffer.width;
}
LOGI(" scale of img = %d", scalew);
for (int i = 0, choosehNum = 0; i < cinfo.output_height; i++) {
jpeg_read_scanlines(&cinfo, buffer, 1);
buffertmp = *buffer;
if (i % scalew == 0 && choosehNum++ < nwBuffer.height) {
//LOGI("nwBuffer->format == WINDOW_FORMAT_RGB_565");
for (int j = 0, choosewNum = 0; j < cinfo.output_width; j++) {
if (j % scalew == 0) {
if (nwBuffer.format == WINDOW_FORMAT_RGB_565) {
line[choosewNum] = ((__uint16_t ) buffertmp[3 * j + 0]
& get8h5) << 8
| ((__uint16_t ) (buffertmp[3 * j + 1] & get8h6)
<< 3)
| ((__uint16_t ) (buffertmp[3 * j + 2] & get8h6)
>> 3);
choosewNum++;
}
}
}
line = line + nwBuffer.stride;
}
}
(void) jpeg_finish_decompress(&cinfo);
LOGI("jpeg_finish_decompress !!");
jpeg_destroy_decompress(&cinfo);
LOGI("jpeg_destroy_decompress !!");
/* Close files, if we opened them */
fclose(input_file);
return 0;
}
Hope to help you!