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

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

选择完后,自动显示:

时间: 2024-10-13 02:10:47

Android NDK使用libjpeg解析JPEG图片,并显示到SurfaceView上的相关文章

Android中ImnageView 中如何让图片拉伸显示?

通过设置android:scaleType="fitXY"使得图片拉伸显示.补充:scaleType的属性有matrix(默认).center.centerCrop.centerInside.fitCenter.fitEnd.fitStart.fitXY.android:scaleType="center"保持原图的大小,显示在ImageView的中心.当原图的size大于ImageView的size,超过部分裁剪处理. android:scaleType=&quo

Android xUtils3源码解析之图片模块

初始化 x.Ext.init(this); public static void init(Application app) { TaskControllerImpl.registerInstance(); if (Ext.app == null) { Ext.app = app; } } public final class TaskControllerImpl implements TaskController { public static void registerInstance()

通过 PC 远程控制 Android 的应用 -- 可以将手机屏幕投射显示到电脑上

测试结果中的部分测试图:Mobizen手机界面: 电脑界面: 主界面 视频 全屏视频 WebKey手机界面: 电脑界面: AirMore手机界面: 电脑界面:主界面 镜像 全屏镜像 Airdroid手机界面: 电脑界面:Web主界面 (含AirMirror) 局域网主界面 屏幕截图 全屏屏幕截图 Windows 10 周年版新增 Connect(连接) APP 可以把 Android 手机屏幕传送到电脑 Windows 操作中心 - 连接 - 投影到这台电脑:   “开始”菜单 - 连接 手机“

Android xUtils3源码解析之数据库模块

xUtils3源码解析系列 一. Android xUtils3源码解析之网络模块 二. Android xUtils3源码解析之图片模块 三. Android xUtils3源码解析之注解模块 四. Android xUtils3源码解析之数据库模块 配置数据库 DbManager.DaoConfig daoConfig = new DbManager.DaoConfig() .setDbName("test.db") .setDbVersion(1) .setDbOpenListe

Android xUtils3源码解析之注解模块

xUtils3源码解析系列 一. Android xUtils3源码解析之网络模块 二. Android xUtils3源码解析之图片模块 三. Android xUtils3源码解析之注解模块 四. Android xUtils3源码解析之数据库模块 初始化 public class BaseActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { su

【STM32H7教程】第58章 STM32H7的硬件JPEG应用之图片解码显示

完整教程下载地址:http://www.armbbs.cn/forum.php?mod=viewthread&tid=86980 第58章       STM32H7的硬件JPEG应用之图片解码显示 本章节为大家讲解硬件JPEG的图片解码功能,实际项目用到的地方比较多,一方面通过硬件JPEG可以加速图片解码,另一方面是JPEG图片比较小. 58.1 初学者重要提示 58.2 硬件JPEG驱动设计 58.3 硬件JPEG驱动移植和使用 58.4 实验例程设计框架 58.5 实验例程说明(MDK)

Delphi 读取 c# webservice XML的base64编码图片字符串转化图片并显示

Delphi 读取 c# webservice XML的base64编码图片字符串转化图片并显示 在 开发中遇到应用c#及asp.net的在的webservice 保存图片并以xml文件形式现实出来 并用delphi调用 的方法: 1.c#的webservice现实见 http://www.greensoftcode.net/techntxt/20127118441582992373 之前的文章. 2.在客户端用delphi显示 发现asp.net xml形式输出的图片是base64字符串形式.

如何将位置信息写入JPEG图片文件【android】

通过ExifInterface可以将拍照时的一些属性信息写入图片文件里,其中包括经纬度信息.本文介绍一种将经纬度坐标写入JPEG图片文件的方法! 核心代码 /** * 浮点型经纬度值转成度分秒格式 * * @param coord * @return */ public String decimalToDMS(double coord) { String output, degrees, minutes, seconds; // gets the modulus the coordinate d

分享Android NDK技术详解及应用(Android加壳图片处理性能优化)

1.课程研发环境案例源代码编译和运行环境以JDK1.7和android-sdk-23以及android-ndk-10e版本为基准, ,以下环境都适用于项目.开发工具:android studio 1.5正式版, QT 5.0,SourceInsight 3.5;其他工具:使用到了IDEA PRO工具以及www.androidxref.com网站查看分析源码.2.内容简介本课程主要讲解NDK技术的基本使用方法,如基本常用的JNI函数.Android系统中能使用的本地库的使用方法和注意事项以及GCC