之前我们比较多的介绍视频的渲染和处理,本文我们想谈一谈图片,和视频比起来,图片确实相对简单点,我们知道视频本质上是一帧帧的“图片”组成的,都了解了视频了,图片还需要去了解吗?图片的渲染和视频有相通之处,也有其独特的特点。
视频的渲染一般都是实时渲染,使用SurfaceView或者TextureView,图片的渲染一般都会采用ImageView,可以设置路径,也可以设置Bitmap,再加上Canvas和Paint,我无敌了,可以实现多张多样的效果。ImageView是不是真的无敌?
从上面的聊天我们已经得知ImageView处理图片的的两个问题:
ImageView渲染图片和离屏渲染怎么关联起来了?OpenGL有离屏渲染的概念,顾名思义为屏幕外的渲染,即在当前屏幕缓冲区以外,新开辟一个新缓冲区进行操作。离屏渲染发生在GPU层面上,会创建新的渲染缓冲区,会触发 OpenGL 的多通道渲染管线,图形上下文的切换会造成额外的开销,增加 GPU 工作量。其实从描述上来看,就知道离屏渲染是比较影响性能的。
与离屏渲染相反的就是实时渲染,或者称当前屏幕渲染,CPU计算好frame等属性,将计算好的内容提交给GPU去渲染,GPU渲染完成之后就会放入屏幕帧缓冲区,然后控制器每隔一段时间会去屏幕缓存区读取渲染好的内容,从而显示出来。图片渲染怎么样实现实时渲染?当然是SurfaceView啦,既然是实时渲染,必定有画布的概念,上一篇文章已经非常清楚地指出了SurfaceView的画布本质了。大家有不清楚的可以看一下上一篇文章:为播放器外接一套渲染框架。
我们的印象中SurfaceView通常和视频或者摄像头采集关联比较多,用来渲染图片还是比较少见的。但是为了保证图片实时渲染,SurfaceView确实是一个非常的好的载体。
创建EGL环境
int EGLCore::Init(EGLContext shared_context, int egl_version) {
if (egl_version == 0) {
egl_version = 2;
}
EGLint num_configs;
const EGLint attributes[] = {
EGL_BUFFER_SIZE, 32,
EGL_ALPHA_SIZE, 8,
EGL_BLUE_SIZE, 8,
EGL_GREEN_SIZE, 8,
EGL_RED_SIZE, 8,
EGL_RENDERABLE_TYPE,
EGL_OPENGL_ES2_BIT,
EGL_SURFACE_TYPE,
EGL_WINDOW_BIT,
EGL_NONE
};
if ((display_ = eglGetDisplay(EGL_DEFAULT_DISPLAY)) == EGL_NO_DISPLAY) {
LOGE("eglGetDisplay() returned error %d", eglGetError());
return Error::OPENGL_DISPLAY_ERROR;
}
if (!eglInitialize(display_, 0, 0)) {
LOGE("eglInitialize() returned error %d", eglGetError());
return Error::OPENGL_INITIALIZE_ERROR;
}
if (!eglChooseConfig(display_, attributes, &config_, 1, &num_configs)) {
LOGE("eglChooseConfig() returned error %d", eglGetError());
Release();
return Error::OPENGL_CONFIG_ERROR;
}
EGLint eglContextAttributes[] = { EGL_CONTEXT_CLIENT_VERSION, static_cast<EGLint>(egl_version), EGL_NONE };
context_ = eglCreateContext(display_, config_, shared_context, eglContextAttributes);
if (context_ == nullptr) {
LOGE("eglCreateContext() returned error %d", eglGetError());
Release();
return Error::OPENGL_CREATE_CONTEXT_ERROR;
}
return 0;
}
Surface转NativeWindow,构建EGLSurface
JNIEnv *env = nullptr;
int ret = get_env(&env);
if (env != nullptr) {
native_window_ = ANativeWindow_fromSurface(env, surface);
}
if (ret == JNI_EDETACHED) {
detach_env();
}
EGLSurface EGLCore::CreateWindowSurface(ANativeWindow *window) {
EGLSurface surface = EGL_NO_SURFACE;
EGLint format;
if (window == nullptr) {
LOGE("%s window is null.", __func__);
return surface;
}
if (!eglGetConfigAttrib(display_, config_, EGL_NATIVE_VISUAL_ID, &format)) {
LOGE("%s eglGetConfigAttrib() returned error %d", __func__, eglGetError());
Release();
return surface;
}
ANativeWindow_setBuffersGeometry(window, 0, 0, format);
if (!(surface = eglCreateWindowSurface(display_, config_, window, nullptr))) {
LOGE("%s eglCreateWindowSurface() returned error %d", __func__, eglGetError());
}
return surface;
}
渲染图片纹理
if (render_screen_ == nullptr) {
render_screen_ = new effect::OpenGL();
}
render_screen_->SetOutput(surface_width_, surface_height_);
render_screen_->ProcessImage(process_id);
if (!egl_core_->SwapBuffers(egl_surface_)) {
LOGE("eglSwapBuffers error: %d surface: %d", eglGetError(), (egl_surface_ == EGL_NO_SURFACE));
}
上面最终渲染的process_id就是和FBO绑定的纹理。
通过实现渲染图片我们可以做到什么呢?我们可以实现各种各样的效果。
例如一些视频蒙版效果、滤镜效果、颜色调节效果,下面看一下简单的视频蒙版效果。
通过此文,你可以举一反三,做出一些更有趣的效果。