最近遇到一个需求,要求将一个GL_TEXTURE_2D类型的纹理ID写入到ImageReader生成的Surface中。

其实这个需求与我之前写过的一篇文章 一文学会MediaCodeC与OpenGL录制mp4视频需求比较接近,只需要对该案例源码进行一些改造即可。

在正式介绍实现之前,需先明确:

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

一、Surface

什么是android.view.Surface

  • 用高大上方式(让人听不懂的方式)表述如下:

    android.view.Surface 是 Android 系统中一个重要的图形渲染类,它用于与硬件显示层进行通信,将图形数据渲染到屏幕上,可以用于实现多种功能,如视频播放、相机预览、屏幕录制等。

    Surface 对象代表了一个画布,可以在其上绘制图形,这些图形将通过硬件显示层呈现在屏幕上。可以在 SurfaceViewTextureViewWindowManager 等控件中使用 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() 函数,将 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中,源码案例工程下载地址如下:

https://download.csdn.net/download/aiwusheng/87959680

案例代码实现流程流程如下:

  • OpenGLES3 中加载GL_TEXTURE_2D纹理,生成纹理ID;
  • 通过 EGL 构建 EGLDisplay 并绑定ImageReader提供的Surface
  • 在 EGL 线程中渲染GL_TEXTURE_2D对应的纹理图形;
  • 在 EGL 线程中完成渲染后,通过eglSwapBuffers交换缓冲数据;
  • 在 ImageReader 中 onImageAvailable 中读取Surface数据,并将数据保存为一张Bitmap;
  • 将的Bitmap显示到 ImageView 上(用于验证纹理ID是否正常写入到Surface)

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

= THE END =

一文学会TextureID渲染到Surface的更多相关文章

  1. 一文学会JVM性能优化

    实战性能优化 1 重新认知JVM 之前我们画过一张图,是从Class文件到类装载器,再到运行时数据区的过程,现在咱们把这张图不妨丰富完善一下,展示了JVM的大体物理结构图. 执行引擎:用于执行JVM字 ...

  2. 一文学会 TypeScript 的 82% 常用知识点(下)

    一文学会 TypeScript 的 82% 常用知识点(下) 前端专栏 2019-11-23 18:39:08     都已经 9021 年了,TypeScript(以下简称 TS)作为前端工程师不得 ...

  3. 一文学会text-justify,orientation,combine文本属性

    大家好,我是半夏,一个刚刚开始写文的沙雕程序员.如果喜欢我的文章,可以关注 点赞 加我微信:frontendpicker,一起学习交流前端,成为更优秀的工程师-关注公众号:搞前端的半夏,了解更多前端知 ...

  4. 一文学会MySQL的explain工具

    开篇说明 (1) 本文将细致介绍MySQL的explain工具,是下一篇<一文读懂MySQL的索引机制及查询优化>的准备篇. (2) 本文主要基于MySQL5.7版本(https://de ...

  5. 【Flutter 实战】一文学会20多个动画组件

    老孟导读:此篇文章是 Flutter 动画系列文章第三篇,后续还有动画序列.过度动画.转场动画.自定义动画等. Flutter 系统提供了20多个动画组件,只要你把前面[动画核心](文末有链接)的文章 ...

  6. 一文学会Java的交互式编程环境jshell

    什么是交互式编程环境?重点词交互,在这样的编程环境中,你每输入一行代码,环境都会给你一个反馈,这就是交互式的编程环境.这种编程环境并不太适合工程化的复杂性需求,但在一些快速验证.简单计算之类的场景下还 ...

  7. 一文学会Scala

    整体介绍 Scala 是一门多范式(multi-paradigm)的编程语言,设计初衷是要集成面向对象编程和函数式编程的各种特性. 联邦理工学院洛桑(EPFL)的Martin Odersky于2001 ...

  8. 一文学会matplotlib

    matplotlib基础 “““ 假设一天中每隔两个小时(range(2,26,2))的气温(℃)分别是[15,13,14.5,17,20,25,26,26,27,22,18,15] 用matplot ...

  9. 渲染路径-surface shader 光照函数与渲染路径

    https://docs.unity3d.com/Manual/SL-SurfaceShaderLighting.html Lighting Model declaration Lighting mo ...

  10. 一文学会Java死锁和CPU 100% 问题的排查技巧

    做一个积极的人 编码.改bug.提升自己 我有一个乐园,面向编程,春暖花开 工欲善其事,必先利其器 00 本文简介 作为一名搞技术的程序猿或者是攻城狮,想必你应该是对下面这两个问题有所了解,说不定你在 ...

随机推荐

  1. Linx 阶段一

    Linux Linux常用命令 具体演示 1). ls 2). pwd 3). touch 4). mkdir 5). rm 使用技巧 1. 连按 Tab健自动补齐文件名 2. ll 查看当前目录文件 ...

  2. 笔记:C++学习之旅---指针

    笔记:C++学习之旅---指针 为什么要使用指针 因为在操作大型数据和类时,由于指针可以通过内存地址直接访问数据,从而避免在程序中赋值大量的代码,因此指针的效率最高,一般来说,指针会有三大用途: 1: ...

  3. Django笔记三十二之session登录验证操作

    本文首发于公众号:Hunter后端 原文链接:Django笔记三十二之session登录验证操作 这一篇笔记将介绍 session 相关的内容,包括如何在系统中使用 session,以及利用 sess ...

  4. 音视频八股文(10)-- mp4结构

    介绍 mp4⽂件格式⼜被称为MPEG-4 Part 14,出⾃MPEG-4标准第14部分 .它是⼀种多媒体格式容器,⼴泛⽤于包装视频和⾳频数据流.海报.字幕和元数据等.(顺便⼀提,⽬前流⾏的视频编码格 ...

  5. 【Visual Leak Detector】源码调试 VLD 库

    说明 使用 VLD 内存泄漏检测工具辅助开发时整理的学习笔记.本篇介绍 VLD 源码的调试.同系列文章目录可见 <内存泄漏检测工具>目录 目录 说明 1. VLD 库源码调试步骤 1.1 ...

  6. 2023-02-13:力扣数据中心有 n 台服务器,分别按从 0 到 n-1 的方式进行了编号 它们之间以「服务器到服务器」点对点的形式相互连接组成了一个内部集群 其中连接 connections 是

    2023-02-13:力扣数据中心有 n 台服务器,分别按从 0 到 n-1 的方式进行了编号 它们之间以「服务器到服务器」点对点的形式相互连接组成了一个内部集群 其中连接 connections 是 ...

  7. Django的ModelAdmin自带

    需要自定义数据表中哪些字段可以显示,哪些字段可以编辑,并对数据表中的条目进行排序,同时定义过滤选项.Django的ModelAdmin自带的list_display, list_filter, lis ...

  8. Linux,会这些就够了

    在测试当中,其实对Linux的要求不高,我们在工作中需要记住常用的一些命令,不常用的实际用到的时候再查在记即可,最重要我们要使用命令可以查看日志,定位bug   目录篇: 可用  pwd  命令查看用 ...

  9. Kubernetes GoRoutineMap工具包代码详解

    1.概述 GoRoutineMap 定义了一种类型,可以运行具有名称的 goroutine 并跟踪它们的状态.它防止创建具有相同名称的多个goroutine,并且在上一个具有该名称的 goroutin ...

  10. shell脚本中特殊筛选文件

    问题描述:在写shell中,总会遇到一些各式各样筛选文件的需求,整理了一些特殊情况 1.查找目标文件下大于100Mb的文件 find $target_dir -type f -size +70M 2. ...