最近遇到一个需求,要求将一个GL_TEXTURE_2D类型的纹路ID写入到ImageReader生成的Surface中。 其实这个需求与我之前写过的一篇文章 一文学会MediaCodeC与OpenGL录制mp4视频需求比较接近,只需要对该事例源码进行一些改造即可。

在正式介绍完结之前,需先清晰:

  • 什么是 android.view.Surface
  • 怎么向Surface中写入数据?

一、Surface

什么是android.view.Surface

  • 用高大上办法(让人听不懂的办法)表述如下: android.view.SurfaceAndroid 系统中一个重要的图形烘托类,它用于与硬件显现层进行通信,将图形数据烘托到屏幕上,能够用于完结多种功能,如视频播映、相机预览、屏幕录制等。 Surface目标代表了一个画布,能够在其上制作图形,这些图形将经过硬件显现层呈现在屏幕上。能够在SurfaceViewTextureViewWindowManager等控件中运用Surface进行图形烘托。此外,Surface还能够用于视频播映、相机预览、屏幕录制等功能。
  • 查阅官方描绘、源码完结,总结为一句通俗易懂的话: Surface是一个Java层类,其持有一个Native (C层) 办理的图画缓冲区句柄。由图画缓冲区的顾客创立(如MediaRecorder),经过Surface将句柄传递给图画的生产者 (如MediaPlayer) 进行烘托。

1.1 官方描绘

关于android.view.Surface官方描绘如下:

一文学会TextureID渲染到Surface

翻译过来就是:

Surface 持有一个由屏幕合成器办理(Native层办理)的原始缓冲区的句柄。通常是由图画缓冲区的顾客(如SurfaceTexture、MediaRecorder、Allocation等)创立或生成,并被传递给生产者(如 OpenGL、MediaPlayer、CameraDevice等)进行制作。 由于,Surface 仅仅持有Native层缓冲区的句柄,若Native层的指针被释放后,则该Surface不再有效。

1.2 官方源码

检查Surface 源码,能够看到Surface 经过持有Native层的句柄mNativeObject来办理原始数据缓冲区。

一文学会TextureID渲染到Surface

1.3 官方文档

检查Surface 官方文档,检查其所有的公有办法:

一文学会TextureID渲染到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() 函数,将 EGLSurfaceSurface 目标相关起来。然后就能够经过 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)

事例源码效果图如下图所示:

一文学会TextureID渲染到Surface

= THE END =