您的位置:首页 > 移动开发 > Android开发

Android NDK使用libjpeg解析JPEG图片,并显示到SurfaceView上

2014-12-06 16:40 537 查看
转载请保留原文链接:/article/8338987.html

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代码中进行处理

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展示:

点击显示图片,开始选择图片:




选择完后,自动显示:





感谢大家看完本博客!!如有问题可提出讨论~~

本博客为原创,如果您觉得对您有一些帮助,那就点下面的赞或留言吧!!
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐