Android NDK使用libjpeg解析JPEG图片,并显示到SurfaceView上
SurfaceView大概是谷歌提供给开发者最吸引人的的组件了,原因是SurfaceView的界面刷新允许在非UI线程中更新,正因为此,很多频繁更新界面的应用,如视频播放器、游戏、动画效果总会基于SurfaceView及其子类进行开发。
而最近我正在研究的一个应用是关于处理图片并显示图片的应用,图片实在是内存杀手,而处理图片则运算量非常大,这些都是令人头疼的问题。
分析应用,并选择实现技术
1、处理图片运算量大,为了提高运算效率,选择使用C语言处理图片
2、需要的内存空间较大,为节约内存并提高效率,需要从C语言中读入文件,并及早释放
下面写下展示图片的基本流程
1、用户选择图片
2、获得用户选择的图片的路径
3、调用展示图片的方法(C方法)
第一部分:用户选择图片
Intent intent = new Intent(Intent.ACTION_GET_CONTENT);// ACTION_OPEN_DOCUMENT intent.addCategory(Intent.CATEGORY_OPENABLE); intent.setType("image/jpeg"); if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.KITKAT) { startActivityForResult(intent, SELECT_PIC_KITKAT); } else { startActivityForResult(intent, SELECT_PIC); }
protected void onActivityResult(int requestCode, int resultCode, Intent data) { if (resultCode == RESULT_OK) { if (requestCode == SELECT_PIC) { Uri originalUri = data.getData(); String[] proj = { MediaStore.Images.Media.DATA }; // 好像是android多媒体数据库的封装接口,具体的看Android文档 Cursor cursor = managedQuery(originalUri, proj, null, null, null); // 按我个人理解 这个是获得用户选择的图片的索引值 int column_index = cursor .getColumnIndexOrThrow(MediaStore.Images.Media.DATA); // 将光标移至开头 ,这个很重要,不小心很容易引起越界 cursor.moveToFirst(); // 最后根据索引值获取图片路径 String path = cursor.getString(column_index); Log.v("图片路径: ", path); if (path.endsWith(".jpg")) { isOnActivityResult = true; imgPath = path; } } } };
第三部分:调用展示图片的方法
这个部分需要注意,写在SurfaceHolder的回调方法内,为的是让SurfaceView中的Surface成功建立后,再将Surface传入C代码中进行处理
<span style="white-space:pre"> </span>svShow = (SurfaceView) findViewById(R.id.svShow); svHolder = svShow.getHolder(); svHolder.addCallback(new SurfaceHolder.Callback() { public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { Log.v(TAG, "surfaceChanged format=" + format + ", width=" + width + ", height=" + height); } public void surfaceCreated(SurfaceHolder holder) { Log.v(TAG, "surfaceCreated"); if (isOnActivityResult && imgPath != null) { showJPG(holder.getSurface(), imgPath); } } public void surfaceDestroyed(SurfaceHolder holder) { Log.v(TAG, "surfaceDestroyed"); } });
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); LOGI("初始化信息"); /* Specify data source for decompression */ //指定图片 jpeg_stdio_src(&cinfo, input_file); LOGI("指定图片"); /* Read file header, set default decompression parameters */ (void) jpeg_read_header(&cinfo, TRUE); LOGI("读取头信息, set default decompression parameters "); /* Start decompressor */ (void) jpeg_start_decompress(&cinfo); LOGI("解压"); row_width = cinfo.output_width * cinfo.output_components; LOGI( "图片的宽:%d 图片的高%d 颜色长度:%d", cinfo.output_width, cinfo.output_height, 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"); // tmp = output_buffer; /* 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; } } // memcpy(tmp, *buffer, row_width); // tmp += row_width; (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; }
Demo展示:
点击显示图片,开始选择图片:
选择完后,自动显示:
郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。