GLES学习笔记---OpenGL绘制到ImageReader

一、ImageReader简介

ImageReader 之前经常使用在camera应用里面,创建一个ImageReader,然后获取surface,配流和下发request时候将surface下发给framework,中间具体对ImageReader做了什么没有具体研究过,等到Hal层camera回帧的时候,ImageReader的onFrameAvailable就会回调上来,通过reader就可以获取图片了。

虽然使用上很简单,但是其实ImageReader里面要学习的东西还是很多的。

ImageReader本质上就是封装了BufferQueue和Surface的一些东西,init的时候创建了一个生产者消费者的队列,以下是init的主要代码

二、使用ImageReader接收Opengl绘制结果

为啥用ImageReader接收绘制结果呢?之前写过使用glReadPixels将绘制结果读取到内存,但是glReadPixels在读取较大图片时候很耗时的,尤其是低端机上,因为glReadPixels是直接从显存拷贝到内存,所以并不使用频繁读取的情况。

通过分析ImageReader源码大概可以简单了解到ImageReader基于BufferQueue和Surface的,那我们可以猜到ImageReader相关的内存应该是GraphicBuffer,GraphicBuffer方便于GPU&CPU共享。

下面这段是创建EGL环境的代码,主要就是:

media_status_t status = AImageReader_new(4000, 3000, AIMAGE_FORMAT_RGBA_8888, 3, &imageReader);  创建ImageReader
status = AImageReader_setImageListener(imageReader, &listener);  设置帧回调
ANativeWindow *nwin;
status = AImageReader_getWindow(imageReader, &nwin); 获取ANativeWindow, 作为
eglCreateWindowSurface的输入,也就是我们之后绘制的载体了。
JNIEXPORT void JNICALL Java_com_sprd_opengl_test_MyNdk_init2(JNIEnv *env, jobject obj, jobject surface) {// egl ------------------------------------------------------------------- startLOGD("init");AImageReader *imageReader;media_status_t status = AImageReader_new(4000, 3000, AIMAGE_FORMAT_RGBA_8888, 3, &imageReader);LOGD("AImageReader_new status: %d", status);listener.onImageAvailable = onFrameAvailable;status = AImageReader_setImageListener(imageReader, &listener);LOGD("AImageReader_setImageListener status: %d", status);ANativeWindow *nwin;status = AImageReader_getWindow(imageReader, &nwin);LOGD("AImageReader_getWindow status: %d", status);gl_cxt.nw = nwin;gl_cxt.reader = imageReader;EGLDisplay display = eglGetDisplay(EGL_DEFAULT_DISPLAY);if (display == EGL_NO_DISPLAY) {LOGD("egl display failed");return;}if (EGL_TRUE != eglInitialize(display, 0, 0)) {LOGD("eglInitialize failed");return;}EGLConfig eglConfig;EGLint configNum;EGLint configSpec[] = {EGL_RED_SIZE, 8,EGL_GREEN_SIZE, 8,EGL_BLUE_SIZE, 8,EGL_ALPHA_SIZE, 8,EGL_SURFACE_TYPE, EGL_WINDOW_BIT,EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,EGL_RECORDABLE_ANDROID, EGL_TRUE,EGL_NONE};if (EGL_TRUE != eglChooseConfig(display, configSpec, &eglConfig, 1, &configNum)) {LOGD("eglChooseConfig failed");return;}EGLSurface winSurface = eglCreateWindowSurface(display, eglConfig, nwin, 0);if (winSurface == EGL_NO_SURFACE) {LOGD("eglCreateWindowSurface failed");return;}const EGLint ctxAttr[] = {EGL_CONTEXT_CLIENT_VERSION, 2,EGL_NONE};EGLContext context = eglCreateContext(display, eglConfig, EGL_NO_CONTEXT, ctxAttr);if (context == EGL_NO_CONTEXT) {LOGD("eglCreateContext failed");return;}if (EGL_TRUE != eglMakeCurrent(display, winSurface, winSurface, context)) {LOGD("eglMakeCurrent failed");return;}gl_cxt.display = display;gl_cxt.winSurface = winSurface;gl_cxt.context = context;// egl ------------------------------------------------------------------- end// shader ------------------------------------------------------------------- startGLint vsh = initShader(vertexSimpleShape, GL_VERTEX_SHADER);GLint fsh = initShader(fragSimpleShape, GL_FRAGMENT_SHADER);GLint program = glCreateProgram();if (program == 0) {LOGD("glCreateProgram failed");return;}glAttachShader(program, vsh);glAttachShader(program, fsh);glLinkProgram(program);GLint status2 = 0;glGetProgramiv(program, GL_LINK_STATUS, &status2);if (status2 == 0) {LOGD("glLinkProgram failed");return;}gl_cxt.program = program;LOGD("glLinkProgram success");// shader ------------------------------------------------------------------- end
}

这段是绘制的代码,里面有不少无用的代码,主要是绘制完成之后

eglSwapBuffers一下就可以了,这样才会触发onFrameAvailable回调。
JNIEXPORT void JNICALL Java_com_sprd_opengl_test_MyNdk_process2(JNIEnv *env, jobject obj, jobject bitmap) {LOGD("process2  1");glUseProgram(gl_cxt.program);AndroidBitmapInfo bitmapInfo;if (AndroidBitmap_getInfo(env, bitmap, &bitmapInfo) < 0) {LOGE("AndroidBitmap_getInfo() failed ! ");return;}void *bmpPixels;LOGD("process2  2, format: %d, stride: %d", bitmapInfo.format, bitmapInfo.stride);AndroidBitmap_lockPixels(env, bitmap, &bmpPixels);LOGD("process2  3");unsigned int textureId;glGenTextures(1, &textureId);glActiveTexture(GL_TEXTURE0);glBindTexture(GL_TEXTURE_2D, textureId);glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);int width = bitmapInfo.width;int height = bitmapInfo.height;LOGD("process2  4");glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, bmpPixels);unsigned char *pOri = (unsigned char *)bmpPixels;LOGD("process2  5     %d, %d, %d, %d, %d, %d, %d, %d", *(pOri), *(pOri+1), *(pOri+2), *(pOri+3),*(pOri+114), *(pOri+115), *(pOri+116), *(pOri+117));glBindTexture(GL_TEXTURE_2D, 0);AndroidBitmap_unlockPixels(env, bitmap);unsigned int offTexture, fbo;glGenTextures(1, &offTexture);glActiveTexture(GL_TEXTURE1);glBindTexture(GL_TEXTURE_2D, offTexture);glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);// 没有这句调用, glCheckFramebufferStatus 返回 36054glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);glGenFramebuffers(1, &fbo);LOGD("fb status: %d, error: %d" , glCheckFramebufferStatus(GL_FRAMEBUFFER), glGetError());glBindFramebuffer(GL_FRAMEBUFFER, fbo);glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, offTexture, 0);LOGD("fb status: %d, error: %d" , glCheckFramebufferStatus(GL_FRAMEBUFFER), glGetError());glBindTexture(GL_TEXTURE_2D, 0);glBindFramebuffer(GL_FRAMEBUFFER, 0);/*顶点               纹理*/float vertices[] = {-1.0f, -1.0f, 0.0f, 0.0f, 0.0f,-1.0f, 1.0f,  0.0f, 0.0f, 1.0f,1.0f,  -1.0f, 0.0f, 1.0f, 0.0f,1.0f,  1.0f,  0.0f, 1.0f, 1.0f};unsigned int indices[] = {0, 1, 2, // first triangle1, 2, 3  // second triangle};// optimalunsigned int VBO, EBO, VAO;glGenVertexArrays(1, &VAO);glBindVertexArray(VAO);glGenBuffers(1, &VBO);glBindBuffer(GL_ARRAY_BUFFER, VBO);glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 5 * sizeof(float), (void*)0);glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 5 * sizeof(float), (void*)((0 + 3)*sizeof(float)));glEnableVertexAttribArray(0);glEnableVertexAttribArray(1);glGenBuffers(1, &EBO);glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO);glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);glBindVertexArray(0);glBindVertexArray(VAO);glClearColor(1.0f, 1.0f, 1.0f, 1.0f);glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);// draw to offline texture.glActiveTexture(GL_TEXTURE1);glBindTexture(GL_TEXTURE_2D, offTexture);glBindFramebuffer(GL_FRAMEBUFFER, fbo);LOGD("fb status: %d, error: %d" , glCheckFramebufferStatus(GL_FRAMEBUFFER), glGetError());glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, (void*)0);glBindFramebuffer(GL_FRAMEBUFFER, 0);glBindTexture(GL_TEXTURE_2D, 0);glActiveTexture(GL_TEXTURE0);glBindTexture(GL_TEXTURE_2D, textureId);glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, (void*)0);glBindTexture(GL_TEXTURE_2D, 0);glFinish();glDisableVertexAttribArray(0);glDisableVertexAttribArray(1);glBindVertexArray(0);eglSwapBuffers(gl_cxt.display, gl_cxt.winSurface);LOGD("process2 X");

最后就是onFrameAvailable回调了,绘制完成之后onFrameAvailable就会触发,可以暂时不考虑我的memcpy那一段

AHardwareBuffer_lock耗时就十几毫秒(4000*3000*4的数据),所以很快就能从内存得到绘制结果了。
static void onFrameAvailable(void *context, AImageReader *reader) {LOGD("MyNdk onFrameAvailable");AImage *image = nullptr;media_status_t status = AImageReader_acquireNextImage(reader, &image);LOGD("AImageReader_acquireNextImage status: %d, image: %p", status, image);AHardwareBuffer *inBuffer = nullptr;status = AImage_getHardwareBuffer(image, &inBuffer);LOGD("AImage_getHardwareBuffer status: %d, inBuffer: %p", status, inBuffer);int width;int height;AImage_getWidth(image, &width);AImage_getHeight(image, &height);unsigned char *ptrReader = nullptr;unsigned char *dstBuffer = static_cast<unsigned char *>(malloc(width * height * 4));LOGD("AHardwareBuffer_lock E, w:%d, h:%d", width, height);AHardwareBuffer_lock(inBuffer, AHARDWAREBUFFER_USAGE_CPU_READ_OFTEN, -1, nullptr,(void **) &ptrReader);LOGD("AHardwareBuffer_lock X");memcpy(dstBuffer, ptrReader, width * height * 4);AHardwareBuffer_unlock(inBuffer, nullptr);LOGD("%d, %d, %d, %d", *ptrReader, *(ptrReader+1), *(ptrReader+2), *(ptrReader+3));AImage_delete(image);
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/579287.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

MYSQL数据库的备份与恢复-数据库实验七

一、实验目的 1. 了解备份和恢复的基本概念。 2. 掌握使用MySQL命令进行数据库备份的操作方法。 3. 掌握使用MySQL命令进行数据库恢复的操作方法。 二、实验内容 1. 使用mysqldump命令备份数据库studentsdb的所有表&#xff0c;存于D:\下&#xff0c;文件名为all_tables.s…

C#与php自定义数据流传输

C#与php自定义数据流传输 介绍一、客户端与服务器数据传输流程图客户端发送数据给服务器&#xff1a;服务器返回数据给客户端&#xff1a; 二、自定义数据流C#版本数据流PHP版本数据流 三、数据传输测试1.在Unity中创建一个C#脚本NetWorkManager.cs2.服务器www目录创建StreamTe…

华清远见嵌入式学习——ARM——作业4

作业要求&#xff1a; 代码运行效果图&#xff1a; 代码&#xff1a; do_irq.c: #include "key_it.h" extern void printf(const char *fmt, ...); unsigned int i 0;//延时函数 void delay(int ms) {int i,j;for(i0;i<ms;i){for(j0;j<2000;j);} }void do_i…

基于AR+地图导航的景区智慧导览设计

随着科技的飞速发展&#xff0c;智慧旅游已经成为现代旅游业的一个重要趋势。在这个背景下&#xff0c;景区智慧导览作为智慧旅游的核心组成部分&#xff0c;正逐渐受到越来越多游客的青睐。本文将深入探讨地图导航软件在景区智慧导览中的应用&#xff0c;并分析其为游客和景区…

【JavaWeb学习笔记】18 - 文件上传下载

项目代码 https://github.com/yinhai1114/JavaWeb_LearningCode/tree/main/fileupdown 目录 文件上传 一、基本介绍 二、文件上传的基本原理 ​编辑 三、文件上传应用实例 四、文件上传的注意细节 1.解决中文乱码问题 2.分割文件夹 3.防止重名 4.百度WebUploader 5.空…

redis 从0到1完整学习 (七):ZipList 数据结构

文章目录 1. 引言2. redis 源码下载3. zipList 数据结构3.1 整体3.2 entry 数据结构分析3.3 连锁更新 4. 参考 1. 引言 前情提要&#xff1a; 《redis 从0到1完整学习 &#xff08;一&#xff09;&#xff1a;安装&初识 redis》 《redis 从0到1完整学习 &#xff08;二&am…

常用API:Object

Object Object类的作用&#xff1a; Object类是Java中所有类的祖宗类&#xff0c;因此&#xff0c;Java中所有的对象都可以直接使用Object类中提供的一些方法。 Object类的常见方法 方法名说明public String toString&#xff08;&#xff09;返回对象的字符串表示形式publi…

kubernetes -pod 实践

一、资源与对象 1、pod 容器都是由镜像启动的,但在容器外面会包裹通过Pod将容器包裹起来这个是K8s的概念,在这个Pod里面可以有一个或多个容器,那这个Pod的有什么特征呢 Pod里的所有容器都会调度在同一个节点上运行0。Pod中的所有容器会共享同一网络,它们有一个唯一的IP,…

PDF编辑工具--Acrobat Pro DC 2023中文

Acrobat Pro DC 2023是一款功能强大的PDF编辑和管理软件&#xff0c;它可以帮助用户在创建、编辑、转换和共享PDF文档方面达到前所未有的高度。这款软件提供了丰富的编辑功能&#xff0c;使用户能够轻松添加注释、高亮、下划线、插入文本等&#xff0c;自由地编辑PDF文档。除了…

枚举的使用

背景以及定义 枚举是在jdk1.5以后引入的.主要用途是:将常量组织起来,在这之前表示一组常量通常使用定义常量的方式: public static final int RED 1; public static final int GREEN 2; public static final int BLUE 3; 但是常量举例有不好的地方,例如:可能碰巧有一个数字…

【Web API系列】使用getDisplayMedia来实现录屏功能

文章目录 前言一、认识getD该处使用的url网络请求的数据。二、使用步骤1.使用方法一实现录屏2.使用方法二实现录屏3. 运行效果 延伸 前言 Web API经过长期的发展&#xff0c;尤其是最近&#xff0c;发展相当迅猛&#xff0c;现在已经支持很多功能了&#xff0c;一些原生就支持…

[Linux]——彻底学通权限

学习权限 一、权限概念二、权限管理2.1文件访问者分类&#xff08;人&#xff09;2.2文件类型和访问权限&#xff08;事物的属性&#xff09;2.3 文件访问权限的相关设置方法 三、目录的权限3.1、进入目录的权限3.2、粘滞位 四、关于权限的总结 一、权限概念 Linux下有两种用户…

【AI】Langchain-Chatchat搭建本地知识库-未完,先记录踩的坑

事先说一下&#xff0c;我本地的显卡4070只有12G显存&#xff0c;无法运行本地知识库&#xff0c;我把自己折腾的过程和遇到的坑先记录一下吧&#xff0c;后续如果有算力的话就再跑一遍试试。后续来了&#xff1a;【AI】使用阿里云免费服务器搭建Langchain-Chatchat本地知识库 …

阿赵UE学习笔记——4、新建关卡

阿赵UE学习笔记目录 大家好&#xff0c;我是阿赵。   之前介绍了虚幻引擎的常用窗口功能&#xff0c;这次开始创建游戏内的世界了。首先先从创建关卡开始。 一、创建新关卡 在使用UE引擎制作游戏&#xff0c;首先要有一个场景作为基础&#xff0c;这个场景在UE里面成为关卡。…

带你认识 WIDGET、WINDOW 、FRAME和 FRAMEGROUP

1、概述 在 YonBuilder 移动开发中&#xff0c;开发者需要了解一些常用的概念术语&#xff0c;其中和App整体框架结构及页面层级组成相关最重要几个重要概念&#xff0c;就是 Widget、 Window 和 Frame、frameGroup。掌握了这几个概念&#xff0c;对于开发者开发 App 时的 API…

linux离线安装jdk11

1. 下载java11&#xff0c;Java Downloads | Oracle 2. 文件安装 jdk11&#xff1a; mkdir /usr/lib/jvm/ 将下载的文件&#xff0c;解压到/usr/lib/jvm/下 tar -zxf jdk-11.0.21_linux-x64_bin.tar.gz -C /usr/lib/jvm/ 3. 将以下命令写入bashrc文件 export JAVA_HOME/us…

26、湾湾国立阳明交通大学、湾湾长庚纪念医院提出:ALL Attention U-Net,独属头部CT分割的[玛格丽特]

本文由台湾国立阳明交通大学、台湾长庚纪念医院于2023年12月16日在arXiv<Image and Video Processing>发表。 论文地址&#xff1a; 2312.10483.pdf (arxiv.org) 0、Abstract 脑出血在 Head CT扫描中作为第一线工具&#xff0c;帮助专家诊断不同类型的出血。然而&…

Tomcat面试题(10道含答案),由浅入深

请解释Tomcat的基本概念和作用 Tomcat是一个开源的Java Web服务器和Servlet容器&#xff0c;用于提供基于Java的应用程序运行环境。它支持Java Servlet规范&#xff0c;使得开发者能够快速构建和部署基于Web的应用程序。 请描述Tomcat的目录结构&#xff0c;并解释各个目录的…

cmd启动Java项目提示:jar中没有主清单属性

1、问题 2、原因 在IDEA中开发SpringBoot项目并打成jar包&#xff0c; 需要添加springboot打包插件&#xff0c;如果不添加&#xff0c;仅仅用maven进行打包&#xff0c;打成包里面是少文件的。 <build><plugins><!--springboot打包插件--><plugin>&…

ElasticSearch之RestClient笔记

1. ElasticSearch 1.1 倒排索引 1.2 ElasticSearch和Mysql对比 1.3 RestClient操作 导入依赖 <dependency><groupId>org.elasticsearch.client</groupId><artifactId>elasticsearch-rest-high-level-client</artifactId><version>7.15.…