通过上一篇博客我们了解了,window和Viewroot的创建过程和作用,此时我们是否考虑过我们的布局文件中的view类是如何显示的,我们现在可知的是布局文件中的view类已经存放在Viewroot里面了,随后的工作系统应该完成所有view类绘制,如何绘制和显示是我们本篇博客讨论的重点
了解显示系统需要从三方面入手:
(1)图像引擎skia
(2)OpenGL ES
(3)surface
Android使用图像引擎skia 作为其核心图形引擎。Skia 图形渲染引擎最初由 Skia 公司开
发,该公司于2005 年被 Google 收购。skia是作为一个第三方组件放在external目录下面
其实主要涉及到的3个库:
libcorecg.so 包含/skia/src/core的部分内容,比如其中的Region,Rect是在SurfaceFlinger里面计算可是区域的操作基本单位;
libsgl.so 包含/skia/src/core|effects|images|ports|utils的部分和全部内容,这个实现了skia大部分的图形效果,以及图形格式的编辑
libskiagl.so 包含/skia/src/gl里面的内容,主要用来调用opengl实现部分效果。
。skia结构如图:
(图片来自于网络)
Skia作为android图像引擎在处理部分,只要是C++代码实现,作为第三方组件提供给android使用,作为组件提供的框架代码最好提供一个很好的设计方式给编程者调用,从编程角度来考虑Skia
(1)输入输出明确
(2)接口封装功能完善
Skia组件可以想象成图像加工工厂,把原料加入进去,出来的是想要的产品,想要什么样的产品可以通过接口调用来完成
如图:
从图可知我们给Skia传递一个bitmap或者device,我们传递的是bitmap或者device的地址,就像我们C语言的指针传递一样,给定了bitmap或者device最后通过接口的加工完成图像的绘制工作
Skia的功能只是完成图像的绘制加工,至于完成后的bitmap如何放在LCD上显示,是surface的工作。
Android 3D 引擎采用的是OpenGL ES。OpenGL ES是一套为手持和嵌入式系统设计的3D引擎API,由Khronos公司维护,EGL 是 OpenGL ES 和底层 Native 平台视窗系统之间的接口,EGL 主要构成( Display , Context , Configuration )
EGL 相当作用是将OpenGL ES封装,外部使用OpenGL ES,只要初始化EGL,通过EGL来使用OpenGL ES
Android中初始化EGL代码:
ViewRoot.java (frameworksbasecorejavaandroidview)
private void initializeGLInner() {
final EGL10 egl = (EGL10) EGLContext.getEGL();
mEgl = egl;
// 获取Display,这里的Display对应的是显示的硬件
final EGLDisplay eglDisplay = egl.eglGetDisplay(EGL10.EGL_DEFAULT_DISPLAY);
mEglDisplay = eglDisplay;
// 初始化egl,完成一些初始化工作
int[] version = new int[2];
egl.eglInitialize(eglDisplay, version);
// 选择Config,其实就是设置一些FrameBuffer相关参数
final int[] configSpec = {
EGL10.EGL_RED_SIZE, 5,
EGL10.EGL_GREEN_SIZE, 6,
EGL10.EGL_BLUE_SIZE, 5,
EGL10.EGL_DEPTH_SIZE, 0,
EGL10.EGL_NONE
};
final EGLConfig[] configs = new EGLConfig[1];
final int[] num_config = new int[1];
egl.eglChooseConfig(eglDisplay, configSpec, configs, 1, num_config);
final EGLConfig config = configs[0];
// Context就代表这个状态机,程序的主要工作就是向Context提供状态,也从Context里获取一些信息。一般状态机的操作是通过命令的方式,类似的例子如opencore
final EGLContext context = egl.eglCreateContext(eglDisplay, config,
EGL10.EGL_NO_CONTEXT, null);
mEglContext = context;
// 获取EGLSurface,这里的EGLSurface显示的硬件对应的FrameBuffer
final EGLSurface surface = egl.eglCreateWindowSurface(eglDisplay, config, mHolder, null);
mEglSurface = surface;
// 将状态机和EGLSurface绑定
egl.eglMakeCurrent(eglDisplay, surface, surface, context);
// 获取GL11,也就是3D引擎接口,通过接口操作3D
final GL11 gl = (GL11) context.getGL();
mGL = gl;
mGlCanvas = new Canvas(gl);
mUseGL = true;
}
在应用层由三种显示相关的view:
(1) 普通的view类
(2) SurfaceView
(3) GLSurfaceView
通过上一篇博客了解到,这三种view类的都会通过setContentView加入到ViewRoot,由ViewRoot统一管理显示,ViewRoot如何区分显示的看如下代码:
ViewRoot.java (frameworksbasecorejavaandroidview)
private void draw(boolean fullRedrawNeeded) {
。。。。。。
// 如果是 GLSurfaceView使用3D绘图接口绘制
if (mUseGL) {
if (!dirty.isEmpty()) {
Canvas canvas = mGlCanvas;
if (mGL != null && canvas != null) {
mGL.glDisable(GL_SCISSOR_TEST);
mGL.glClearColor(0, 0, 0, 0);
mGL.glClear(GL_COLOR_BUFFER_BIT);
mGL.glEnable(GL_SCISSOR_TEST);
mAttachInfo.mDrawingTime = SystemClock.uptimeMillis();
mAttachInfo.mIgnoreDirtyState = true;
mView.mPrivateFlags |= View.DRAWN;
int saveCount = canvas.save(Canvas.MATRIX_SAVE_FLAG);
try {
canvas.translate(0, -yoff);
if (mTranslator != null) {
mTranslator.translateCanvas(canvas);
}
canvas.setScreenDensity(scalingRequired
? DisplayMetrics.DENSITY_DEVICE : 0);
mView.draw(canvas);
if (Config.DEBUG && ViewDebug.consistencyCheckEnabled) {
mView.dispatchConsistencyCheck(ViewDebug.CONSISTENCY_DRAWING);
}
} finally {
canvas.restoreToCount(saveCount);
}
mAttachInfo.mIgnoreDirtyState = false;
// eglSwapBuffers,将数据送给FrameBuffer显示
mEgl.eglSwapBuffers(mEglDisplay, mEglSurface);
checkEglErrors();
。。。。。。
// 如果是 普通的view类或者SurfaceView都是有2D接口显示
Canvas canvas;
try {
int left = dirty.left;
int top = dirty.top;
int right = dirty.right;
int bottom = dirty.bottom;
// 通过surface获取canvas
canvas = surface.lockCanvas(dirty);
。。。。。。
// 从应用层传入的view都放在ViewGroup里面,mView为视图组,当调用mView是会调用dispatchDraw函数遍历的调用视图组中每个view的draw()
mView.draw(canvas);
} finally {
mAttachInfo.mIgnoreDirtyState = false;
canvas.restoreToCount(saveCount);
}
if (Config.DEBUG && ViewDebug.consistencyCheckEnabled) {
mView.dispatchConsistencyCheck(ViewDebug.CONSISTENCY_DRAWING);
}
if (Config.DEBUG && ViewDebug.showFps) {
int now = (int)SystemClock.elapsedRealtime();
if (sDrawTime != 0) {
nativeShowFPS(canvas, now - sDrawTime);
}
sDrawTime = now;
}
if (Config.DEBUG && ViewDebug.profileDrawing) {
EventLog.writeEvent(60000, SystemClock.elapsedRealtime() - startTime);
}
}
// 用完以后释放Canvas,Canvas供多个surface使用
} finally {
surface.unlockCanvasAndPost(canvas);
}
。。。。。。
}
2D引擎,3D引擎和surface关系如图:
剩下的任务主要是分析surface:
原理 分析
让我们首先看一android屏幕示意图:
每一个应用程序对应一个或者多个activity,每个activity对应一个或者多个图形界面,每个图形界面我们称为surface
在上面的图中我们能看到3个surface ,一个是home 界面,还有就是红、蓝、,而两个控件实际是home surface 里面的内容,3个surface相互重叠在一起,系统是如何解决图层的重合,是surface系统的关键
(1)surface在android中解释为一个显示模块的封装,一个显示模块在android中需要一个合理的类来描述其信息,这里面和显示相关的信息有如下内容:在屏幕上有它要显示的内容,大小,位置这些元素,surface类就是封装了一个显示模块的这些信息,其中需要一个结构来记录应用程序界面的位置,大小,以及一个buffer 来记录需要显示的内容,所以这就是我们surface 的概念,
(2)多个surface显示会出现重叠的作用,这里需要一个管理者来管理这些surface的重叠显示,通过上面的博客的阅读,我们大致可以想到android会使用一种服务的方式去管理这些surface的显示,并且各个surface和管理者通过binder通信的方式进行通信,在android中起到管理者作用的是SurfaceFlinger,它把各个surface 组合(compose/merge) 成一个main Surface ,最后将Main Surface 的内容发送给FB/V4l2 Output ,这样屏幕上就能看到我们想要的效果。
在实际中对这些Surface 进行merge 可以采用两种方式,一种就是采用软件的形式来merge ,还一种就是采用硬件的方式,软件的方式就是我们的SurfaceFlinger ,而硬件的方式就是Overlay 。(Overlay的具体实现的内容将在下一篇的博客中详细讲解)
Surface类创建过程分析:
Surface.java (frameworksbasecorejavaandroidview)
这个文件为Surface类的实现代码,也是我们在java层可以直接使用的代码
构造函数代码如下:
public Surface(SurfaceSession s,
int pid, int display, int w, int h, int format, int flags)
throws OutOfResourcesException {
mCanvas = new CompatibleCanvas();
init(s,pid,display,w,h,format,flags);
}
init(s,pid,display,w,h,format,flags);//为JNI层代码在android_view_Surface.cpp中实现
android_view_Surface.cpp (frameworksbasecorejni)
Surface java层函数和JNI层函数调用的映射表如下:
static JNINativeMethod gSurfaceMethods[] = {
{"nativeClassInit", "()V", (void*)nativeClassInit },
{"init", "(Landroid/view/SurfaceSession;IIIIII)V", (void*)Surface_init },
{"init", "(Landroid/os/Parcel;)V", (void*)Surface_initParcel },
。。。。。。
};
static void Surface_init(
JNIEnv* env, jobject clazz,
jobject session, jint pid, jint dpy, jint w, jint h, jint format, jint flags)
{
if (session == NULL) {
doThrow(env, "java/lang/NullPointerException");
return;
}
//获取java层SurfaceSession变量 mClient域
SurfaceComposerClient* client =
(SurfaceComposerClient*)env->GetIntField(session, sso.client);
//创建一个surface
sp
if (surface == 0) {
doThrow(env, OutOfResourcesException);
return;
}
setSurfaceControl(env, clazz, surface);
}end
*******
SurfaceComposerClient* client =
(SurfaceComposerClient*)env->GetIntField(session, sso.client);
这段代码如何取得SurfaceComposerClient实例的引用,这里解释一下首先在java层当我们调用surface的构造函数时候
public Surface(SurfaceSession s,
int pid, int display, int w, int h, int format, int flags)
,需要传递一个SurfaceSession类的引用SurfaceSession类调用如下:
SurfaceSession.java (frameworksbasecorejavaandroidview)
public class SurfaceSession {
public SurfaceSession() {
init();
}
protected void finalize() throws Throwable {
destroy();
}
private native void init();
private native void destroy()
private int mClient;
}
//函数映射表
static JNINativeMethod gSurfaceSessionMethods[] = {
{"init", "()V", (void*)SurfaceSession_init },
{"destroy", "()V", (void*)SurfaceSession_destroy },
{"kill", "()V", (void*)SurfaceSession_kill },
};
static void SurfaceSession_init(JNIEnv* env, jobject clazz)
{
sp
client->incStrong(clazz);
env->SetIntField(clazz, sso.client, (int)client.get());//将SurfaceComposerClient的C++层引用赋值给java层
}
说明:当创建一个surface时,同时创建一个SurfaceComposerClient,SurfaceComposerClient可以看做封装surface类,对于一个surface来说需要和服务器SurfaceFlinger通信,通信在android中需要使用proxy/stub机制,SurfaceComposerClient类本身没有这种机制,但是在它的内部有封装了一些可以和服务器通信的类。
class SurfaceComposerClient : virtual public RefBase
{
。。。。。。
sp
int pid, // pid of the process the surface is for
DisplayID display, // Display to create this surface on
uint32_t w, // width in pixel
uint32_t h, // height in pixel
PixelFormat format, // pixel-format desired
。。。。。
// after assignment
status_t mStatus;
SharedClient* mControl;
sp
sp
sp
};
}; // namespace android
当上图关心建立完成后如下的函数会被调用:
sp
ISurfaceFlingerClient::surface_data_t* params,
DisplayID d, uint32_t w, uint32_t h, PixelFormat format,
uint32_t flags)
{
。。。。。。
switch (flags & eFXSurfaceMask) {
case eFXSurfaceNormal:
if (UNLIKELY(flags & ePushBuffers)) {
layer = createPushBuffersSurfaceLocked(client, d, id,
w, h, flags);
} else {
layer = createNormalSurfaceLocked(client, d, id,
w, h, flags, format);
}
break;
case eFXSurfaceBlur:
layer = createBlurSurfaceLocked(client, d, id, w, h, flags);
break;
case eFXSurfaceDim:
layer = createDimSurfaceLocked(client, d, id, w, h, flags);
break;
}
。。。。。。
}
return surfaceHandle;
}
Android 提供了 4 种类型的 layer 供选择,每个 layer 对应一种类型的窗口,并对应这种窗口相应的操作: Layer , LayerBlur , LayerBuffer , LayerDim 。
Norm Layer 是 Android 种使用最多的一种 Layer ,一般的应用程序在创建 surface 的时候都是采用的这样的 layer ,
Normal Layer 为每个 Surface 分配两个 buffer : front buffer 和 back buffer ,这个前后是相对的概念,他们是可以进行替换的。 Front buffer 用于 SurfaceFlinger 进行显示,而 Back buffer 用于应用程序进行画图,当 Back buffer 填满数据 (dirty) 以后,就会 替换, back buffer 就变成了 front buffer 用于显示,而 front buffer 就变成了 back buffer 用来画图,(这段话引用其他博客文章)
sp
const sp
int32_t id, uint32_t w, uint32_t h, uint32_t flags,
PixelFormat& format)
{
// initialize the surfaces
switch (format) { // TODO: take h/w into account
case PIXEL_FORMAT_TRANSPARENT:
case PIXEL_FORMAT_TRANSLUCENT:
format = PIXEL_FORMAT_RGBA_8888;
break;
case PIXEL_FORMAT_OPAQUE:
format = PIXEL_FORMAT_RGB_565;
break;
}
sp
status_t err = layer->setBuffers(w, h, format, flags);
if (LIKELY(err == NO_ERROR)) {
layer->initStates(w, h, flags);
addLayer_l(layer);
} else {
LOGE("createNormalSurfaceLocked() failed (%s)", strerror(-err));
layer.clear();
}
return layer;
}
Layer类描述如下:
待续。。。。。。