最近遇到一个需求,要求将一个GL_TEXTURE_2D类型的纹路ID写入到ImageReader生成的Surface中。 其实这个需求与我之前写过的一篇文章 一文学会MediaCodeC与OpenGL录制mp4视频需求比较接近,只需要对该事例源码进行一些改造即可。
在正式介绍完结之前,需先清晰:
- 什么是
android.view.Surface
? - 怎么向
Surface
中写入数据?
一、Surface
什么是android.view.Surface
?
- 用高大上办法(让人听不懂的办法)表述如下:
android.view.Surface
是 Android 系统中一个重要的图形烘托类,它用于与硬件显现层进行通信,将图形数据烘托到屏幕上,能够用于完结多种功能,如视频播映、相机预览、屏幕录制等。Surface
目标代表了一个画布,能够在其上制作图形,这些图形将经过硬件显现层呈现在屏幕上。能够在SurfaceView
、TextureView
、WindowManager
等控件中运用Surface
进行图形烘托。此外,Surface
还能够用于视频播映、相机预览、屏幕录制等功能。 - 查阅官方描绘、源码完结,总结为一句通俗易懂的话:
Surface是一个Java层类,其持有一个Native (C层) 办理的图画缓冲区句柄。由图画缓冲区的顾客创立(如MediaRecorder),经过Surface将句柄传递给图画的生产者 (如MediaPlayer) 进行烘托。
1.1 官方描绘
关于android.view.Surface
官方描绘如下:
翻译过来就是:
Surface
持有一个由屏幕合成器办理(Native层办理)的原始缓冲区的句柄
。通常是由图画缓冲区的顾客
(如SurfaceTexture、MediaRecorder、Allocation等)创立或生成,并被传递给生产者
(如 OpenGL、MediaPlayer、CameraDevice等)进行制作。
由于,Surface
仅仅持有Native层缓冲区的句柄,若Native层的指针被释放后,则该Surface
不再有效。
1.2 官方源码
检查Surface
源码,能够看到Surface
经过持有Native层的句柄mNativeObject
来办理原始数据缓冲区。
1.3 官方文档
检查Surface
官方文档,检查其所有的公有办法:
看到上述公有办法后,发现除了经过lockCanvas()
办法能够获取一个Canvas
目标,然后运用drawBitmap()
等API写入图形数据外,并无其他有用的办法,帮助咱们完结TextureID
纹路ID的写入。
二、向Surface写入数据
要向Surface
持有的mNativeObject
句柄中写入图画数据,我现在已知有两种办法:
- 第一种办法是上文提到的经过
Canvas
写入; - 第二种办法是经过
OpenGL
写入,也就是本文的要介绍的重点;
2.1 经过Canvas写入
上文介绍到,在检查Surface
的公有办法后,发现经过lockCanvas()
办法能够获取一个Canvas
目标,然后运用drawBitmap()
等API写入图形数据,操作过程如下:
- 经过
SurfaceHolder
获取Surface
目标; - 经过
Surface
目标的lockCanvas()
办法获取Canvas
目标; - 在
Canvas
上进行制作操作,例如调用drawBitmap()
办法制作位图; - 经过
Canvas
目标的unlockCanvasAndPost()
办法将制作成果提交到Surface
中,从而完结在屏幕上烘托数据。
以下为简单的代码举例:
SurfaceHolder holder = surfaceView.getHolder();
Surface surface = holder.getSurface();
Canvas canvas = surface.lockCanvas(null);
// 在 canvas 上进行制作操作
canvas.drawBitmap(bitmap, 0, 0, null);
surface.unlockCanvasAndPost(canvas);
2.2 经过OpenGL写入
经过OpenGL向Surface中写入数据,其根本原理是:
将Surface绑定到一个EGLSurface上,然后经过OpenGL向EGLSurface烘托数据,终究将成果烘托到相关的Surface上。
具体完结是运用 EGL 提供的 eglCreateWindowSurface()
函数,将 EGLSurface
与 Surface
目标相关起来。然后就能够经过 OpenGL ES 将烘托成果制作到 EGLSurface 中,终究烘托到Surface上。
其代码完结举例如下所示:
// 获取 EGLDisplay 目标
EGLDisplay mEglDisplay = EGL14.eglGetDisplay(EGL14.EGL_DEFAULT_DISPLAY);
// 初始化 EGL 环境
int[] version = new int[2];
EGL14.eglInitialize(mEglDisplay, version, 0, version, 1)
// 配置 attribList
int[] attribList = {
EGL14.EGL_RED_SIZE, 8,
EGL14.EGL_GREEN_SIZE, 8,
EGL14.EGL_BLUE_SIZE, 8,
EGL14.EGL_ALPHA_SIZE, 8,
//
EGL14.EGL_RENDERABLE_TYPE,
EGL14.EGL_OPENGL_ES2_BIT,
0x3142,
1,
EGL14.EGL_NONE
};
EGLConfig[] configs = new EGLConfig[1];
int[] numConfigs = new int[1];
EGL14.eglChooseConfig(mEglDisplay, attribList, 0, configs, 0, configs.length, numConfigs, 0);
// 获取 EGLContext 上下文
EGLContext shareEglContext = inEglContext;
// 配置 EGLContext 属性
final int[] attrib_list = {
EGL14.EGL_CONTEXT_CLIENT_VERSION, 2,
EGL14.EGL_NONE
};
// 获取 EGLDisplay 目标
EGLDisplay eglDisplay = EGL14.eglGetDisplay(EGL14.EGL_DEFAULT_DISPLAY);
// 创立一个 EGLSurface 目标(绑定surface)
EGLSurface eglSurface = EGL14.eglCreateWindowSurface(eglDisplay, configs[0], surface, surfaceAttribs, 0);
// 将 EGLSurface 和 EGLContext 绑定到 EGLDisplay 上
EGL14.eglMakeCurrent(eglDisplay, eglSurface, eglSurface, eglContext);
// 烘托图形 ...
// 交换前后缓冲(将egl烘托成果交换到surface上)
EGL14.eglSwapBuffers(eglDisplay, eglSurface);
三、源码下载
将一个GL_TEXTURE_2D类型的纹路ID写入到ImageReader生成的Surface中,源码事例工程下载地址如下: download.csdn.net/download/ai…
事例代码完结流程流程如下:
- OpenGLES3 中加载GL_TEXTURE_2D纹路,生成纹路ID;
- 经过 EGL 构建 EGLDisplay 并绑定ImageReader提供的Surface
- 在 EGL 线程中烘托GL_TEXTURE_2D对应的纹路图形;
- 在 EGL 线程中完结烘托后,经过eglSwapBuffers交换缓冲数据;
- 在 ImageReader 中 onImageAvailable 中读取Surface数据,并将数据保存为一张Bitmap;
- 将的Bitmap显现到 ImageView 上(用于验证纹路ID是否正常写入到Surface)
事例源码效果图如下图所示: