图像的显示最终都是由显示器完成的,显示器通过接收到的颜色矩阵来进行对应的显示。而颜色矩阵的产生一般有两种,一种是通过GPU来进行渲染生成,另一种是通过CPU来进行渲染生成。其中GPU比较适合来处理这件事情,所以其效率高。(硬件加速也就是指使用GPU来进行渲染加速)
在Android平台上,GPU渲染的API有两套,一套就是OpenGL-ES,另一套就是7.0后推出的Vulkan。目前使用最多的还是OpenGL-ES。
整个渲染流程中主要节点如下:
SurfaceFlinger <- SurfaceView <- Surface <- EGLSurface <- EGLContext <- OpenGL-ES
接入原生渲染体系
Android原生平台上封装了一整套View体系用来进行图像的渲染和显示,所以任何的渲染都必须基于这个体系才能正确的显示出来。
View系统中提供了两个View可以用来进行自定义渲染,一个是SurfaceView,另一个是TextureView。(关于SurfaceView和TextureView可以看我之前写的Android图形架构总览。
只要将图形数据写入到SurfaceView或TextureView的Surface中,那么最终SurfaceFlinger服务就会将图形内容显示到显卡上。
所以对于播放视频来说,就是要将视频每一帧的图像数据写入到SurfaceView或TextureView的Surface中即可。
SurfaceView可以通过addCallback来接收来自原生渲染的生命周期回调,通过getSurface来获取内部的Surface对象。
1 2
| this.getHolder().addCallback(); this.getHolder().getSurface()
|
TextureView可以通过setSurfaceTextureListener来接收生命周期回调,通过getSurfaceTexture可以获取到SurfaceTexture。
1 2
| setSurfaceTextureListener(); Surface surface = new Surface(getSurfaceTexture());
|
Surface与EGLSurface
一个Surface对象,可以关联一个EGLSurface对象(window_surface),window_surface可以通过swap方法将其内部图像缓冲数据传入到Surface中(其它类型的EGLSurface是不可以的)。
1 2 3 4 5 6 7 8
| if (!(eglSurface = eglCreateWindowSurface(display, config, aNativeWindow, 0))) { return; }
ANativeWindow *pNativeWindow = ANativeWindow_fromSurface(jenv, surface);
|
EGLSurface与EGLContext
EGLContext是与线程相关的,一个线程中只能激活一个EGLContext,激活的EGLContext关联一个EGLSurface,激活后,在当前线程调用OpenGL-ES的API都将作用到EGLSurface的缓冲区中。
1 2 3 4 5
|
if (!(context = eglCreateContext(display, config, 0, context_attribs))) { return; }
|
1 2 3 4 5 6 7 8
|
if (!eglMakeCurrent(display, eglSurface, eglSurface, context)) { return; }
|
OpenGL-ES渲染纹理
当我们准备好了EGLSurface和EGLContext后,那么接下来就是创建OpenGL—ES的Program进行纹理的绘制了。
创建Program
创建Vertex着色器
1 2 3 4 5 6 7 8
| precision mediump float; attribute highp vec4 aPosition; attribute highp vec2 aTextureCoord; varying vec2 textureCoordinate; void main() { gl_Position = aPosition; textureCoordinate = aTextureCoord.xy; }
|
创建Fragment着色器
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| precision mediump float; varying highp vec2 textureCoordinate; uniform lowp sampler2D inputTextureY; uniform lowp sampler2D inputTextureU; uniform lowp sampler2D inputTextureV;
void main() { vec3 yuv; vec3 rgb; yuv.r = texture2D(inputTextureY, textureCoordinate).r - (16.0 / 255.0); yuv.g = texture2D(inputTextureU, textureCoordinate).r - 0.5; yuv.b = texture2D(inputTextureV, textureCoordinate).r - 0.5; rgb = mat3(1.164, 1.164, 1.164, 0.0, -0.213, 2.112, 1.793, -0.533, 0.0) * yuv; gl_FragColor = vec4(rgb, 1.0); }
|
创建Program
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| programHandle = OpenGLUtils::createProgram(vertexShader, fragmentShader);
positionHandle = glGetAttribLocation(programHandle, "aPosition");
texCoordinateHandle = glGetAttribLocation(programHandle, "aTextureCoord");
inputTextureHandle[0] = glGetUniformLocation(programHandle, "inputTextureY"); inputTextureHandle[1] = glGetUniformLocation(programHandle, "inputTextureU"); inputTextureHandle[2] = glGetUniformLocation(programHandle, "inputTextureV");
glUseProgram(programHandle);
if (textures[0] == 0) { glGenTextures(3, textures); } for (int i = 0; i < 3; ++i) { glActiveTexture(GL_TEXTURE0 + i); glBindTexture(GL_TEXTURE_2D, textures[i]);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); glUniform1i(inputTextureHandle[i], i); }
|
设置坐标
对于图像来说,首先创建一个平面矩形,然后在其上贴上纹理即可。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| static const float vertices[] = { -1.0f, -1.0f, 1.0f, -1.0f, -1.0f, 1.0f, 1.0f, 1.0f, };
static const float texture_vertices[] = { 0.0f, 1.0f, 1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, };
glVertexAttribPointer(positionHandle, 2, GL_FLOAT, GL_FALSE, 0, vertices); glEnableVertexAttribArray(positionHandle);
glVertexAttribPointer(texCoordinateHandle, 2, GL_FLOAT, GL_FALSE, 0, texture_vertices); glEnableVertexAttribArray(texCoordinateHandle);
|
设置纹理
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
|
const GLsizei heights[3] = { texture->height, texture->height / 2, texture->height / 2}; for (int i = 0; i < 3; ++i) { glActiveTexture(GL_TEXTURE0 + i); glBindTexture(GL_TEXTURE_2D, textures[i]); glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE, texture->pitches[i], heights[i], 0, GL_LUMINANCE, GL_UNSIGNED_BYTE, texture->pixels[i]); glUniform1i(inputTextureHandle[i], i); }
|
绘制
1 2
| glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
|
将OpenGL-ES生成的图像数据加入到EGLSurface绑定的Surface中
1 2 3 4
| if (!eglSwapBuffers(display, eglSurface)) { return; }
|