[转载] android中的surface

时间:2023-02-02 08:05:52
在android中,对view及其子类,都是画在surface上的。每个window对应一个surface,各surface对象通过surfaceflinger合成到framebuffer,每个surface都是双缓冲,它有一个back buffer和一个front buffer。back buffer就是画画的地方,front buffer是用来合成的。surface创建Canvas对象(用来管理surface绘图操作),Canvas对应bitmap(存储surface内容)。当调用unlockCanvas()后,back buffer开始变为可用,就开始显示了。有一套机制实现back buffer和front buffer的互换,当要更新时,back buffer与front buffer互换,back buffer变成front buffer,流程- create a bitmap- attach a canvas to it- do the rendering into that canvas- lockCanvas- draw your bitmap into the backbuffer- unlockAndPostframeworks/base/core/java/android/view/Surface.java — Surface::Surface ()创建一个surface public Surface(SurfaceSession s, int pid, int display, int w, int h, int format, int flags) throws OutOfResourcesException { mCanvas = new Canvas(); init(s,pid,display,w,h,format,flags); }frameworks/base/core/jni/android_view_Surface.cpp — Surface_init ()。在这个函数中SurfaceComposerClient 对象被创建。frameworks/base/libs/ui/SurfaceComposerClient.cpp — SurfaceComposerClient::SurfaceComposerClient ()这个函数非常重要,在这里建立了client和server之间的桥梁。通过函数_get_surface_manager()获得了一个指向 server的IBinder 对象(具有ISurfaceComposer接口),之后通过这个IBinder就可以跨进程访问Server的功能。接着调用 ISurfaceComposer::createConnection()创建并返回了一个ISurfaceFlingerClient的 IBinder。frameworks/base/libs/ui/SurfaceComposerClient.cpp — SurfaceComposerClient::createSurface().这个函数中,利用前面获得的ISurfaceFlingerClient的IBinder,调用其createSurface接frameworks/base/libs/surfaceflinger/SurfaceFlinger.cpp — BClient::createSurface ()。Bclient由ISurfaceFlingerClient派生而来frameworks/base/libs/surfaceflinger/SurfaceFlinger.cpp — SurfaceFlinger:: createSurface()。这个函数为Surface创建一个对应的Layer。Android GUI系统:涉及JAVA框架层的内容: andriod.graphics 类 ,对应Skia底层库,提供绘图接口。 andriod.view.Surface 构建显示界面。 andriod.view.View 各种UI元素的基类。 Javax.microedition.khronos.opengles 标准的OpenGL接口。Pixelflinger是一个底层的工具库。负责像素级别的基本处理。在system/core/include/pixelflinger/ 和/system/core/libpixelflinger/libui是一个Andriod在本地层次的一个框架库,是GUI系统的中枢。这个库提供接口,其它的库通过类的继承方式来实现。包含颜色格式,Egl窗口,按键及事件处理,surface,overlay ,camera等多个方面的定义。在framework/base/include/ui/ 和framework/base/libs/ui/中。包含以下部分: format(颜色格式)部分:需要用pixelflinger中的一些关于数据格式的定义。头文件为PixelFormat.hPoint.h Region.h Rect.h DispleyInfo.h Native Windows (本地窗口)部分:主要是实现下个egl_native_window_t 的类。程序通过调用这个类来完成基本的显示功能。头文件为: EGLNativeSurface.h EGLDisplaySurface.h EGLNativeWindowSurface.h Key/Event(按键和事件处理)部分:系统输入的基础,定义按键映射,通过Event事件设备来实现系统输入,头文件为:EventHub.h KeycodeLabels.h KeyCharacterMap.h Surface(显示界面)部分:本部分定义显示界面较高层次的接口,包含显示界面的管理功能,头文件为带有Surface字符串的所有文件,这部分只是定义了Surface部分的框架,具体实现是SurfaceFlinger。 Overlay(显示部分的叠加层)部分:定义一个叠加的显示输出层接口,覆盖在主显示层之上,通常用于视频输出,主要在SurfaceFlinger中实现。头文件为:IOverlay.h Overlay.h Camera部分:定义摄像头的框架和接口,主要在CameraService部分实现。头文件为带有Camera定符串的所有文件。输入输出的接口:(主要和linux驱动打交道的)使用FrameBuffer的标准显示驱动和标准事件Input驱动,在libui中使用标准方式实现: 1。显示输出的硬件接口: 对于andriod的显示部分,需要实现的接口是egl_native_window_t,它是一个OpenGL结构,也是给libEGL使用的。 EGLNativeSurface.h定义了类EGLNativeSurface,这个类继承了egl_native_windows_t. EGLDisplaySurface.h定义了类EGLDisplaySurface,继承了EGLNativeSurface,它是最终的实现类。 在EGLDisplaySurface.cpp所实现的构造函数中调用mapFrameBuffer()函数对驱动程序进行操作如:status_t EGLDisplaySurface::mapFrameBuffer(){ char const * const device_template[] = { "/dev/graphics/fb%u", "/dev/fb%u", 0 }; while ((fd==-1) && device_template) { snprintf(name, 64, device_template, 0); fd = open(name, O_RDWR, 0); i++; } struct fb_fix_screeninfo finfo; if (ioctl(fd, FBIOGET_FSCREENINFO, &finfo) == -1) return -errno; struct fb_var_screeninfo info; if (ioctl(fd, FBIOGET_VSCREENINFO, &info) == -1) return -errno;void* buffer = (uint16_t*) mmap( 0, finfo.smem_len, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); if (buffer == MAP_FAILED) return -errno; // at least for now, always clear the fb memset(buffer, 0, finfo.smem_len);//省略了部分内容} 2.输入的硬件接口: 对andriod的事件处理部分,主要是向上层提供统一的按键码(KeyCode),是一个整数,上层的Java程序中主要通过这个值来判断系统的实现。在libui中通过对标准的Input驱动的处理来将input值转换成andriod系统的按键码,按键码参考KeyCharacterMay.h头文件。 EventHub.cpp文件是输入部分的硬件抽象定义设备节点所在的路径。 Static const char *device_path = “/dev/input”; //输入设备目录 处理过程中将搜索路径下面所有input驱动设备节点,这在openPlatformInput()中通过调用scan_dir()来实现,scan_dir()将会从目录中查找设备,找到后调用open_device()将其打开。bool EventHub:penPlatformInput(void){//省略了部分内容 res = scan_dir(device_path); if(res < 0) { LOGE("scan dir failed for %s/n", device_path); //open_device("/dev/input/event0"); } return true;} 主要事件处理有getEvent()中完成,处理过程是在一个无限循环内,调用阻塞的函数等待事件到来。bool EventHub::getEvent(int32_t* outDeviceId, int32_t* outType, int32_t* outScancode, int32_t* outKeycode, uint32_t *outFlags, int32_t* outValue, nsecs_t* outWhen){ while(1) {//省略了部分内容 for(i = 1; i < mFDCount; i++) { if(mFDs.revents) { if(mFDs.revents & POLLIN) { res = read(mFDs.fd, &iev, sizeof(iev)); } //省略了部分内容 }}poll()函数会阻塞程序的运行,直到Input设备的相应事件发生,事件发生后poll()将返回,然后通过read()读取input设备发生的事件代码。实现事件处理实际经过两个步骤: 1,将input设备的整数类型事件转换成表示按键的字条串。 2。将表示按键的字符串转换成android的按键码。 键盘布局文件(*.kl)将完成第一步转换,运行时的文本文件在system/usr/keylayout目录中。 源文件的development/emulator/keymaps/目录中有多个布局文件。 第二步是通过查找KEYCODES数组,将literal字符串转换成value的整数值。(在keyCharacterMap.h文件中KEYCODES表示的数值和android.view.KeyEvent类中数值是一致的,这个java类的路径为frameworks/base/core/java/android/view/KeyEvent.java) 开发过程中对不同的硬件,只要定不同的键盘布局文件就OK了。Android中一般不需要增加新的按键码。(在andriod的输入处理经过了两次映射,第一次将Event驱动中的整数按键码映射成字符串,第二次将字符串映射成Java的UI 程序中使用的整数值。如要增加按键在用户程序 中进行处理除了KeyCharacterMap.h和KeyEvent.java两个文件 ,还要改两个文件 tools/puppet_master/PuppetMaster.nav_keys.py和frameworks/base/core/res/res/values/attrs.xmlSurface系统:包括本地代码和java代码部分,关系如下: libui提供本地的Surface系统框架 surfaceflinger完成本地接口实现 java框架层次主要调用Surface向UI 提供接口 本地部分可以使用ISurface接口。Surface系统本地接口 在libui中定义Surface的本地接口,路径frameworks/base/include/ui,主要有以下几个文件; Surface.h SurfaceComposerClient.h ISurface.h ISurfaceFlingerClient.h IsurfaceComposer.hSurface.h和 SurfaceComposerClient.h是为上层提供的调用接口通过surface系统的JNI提供给java层使用。ISurface.h IsurfaceFlingerClient.h IsurfaceComposer.h 是需要下层去继承和实现的接口,其中 Isurface.h 中的接口可以给本地程序来调用,进而实现图形数据输出功能。在isurfaceComposeer.h接口中,定义了Surface系统的各种枚举值和接口,class ISurfaceComposer : public IInterface{public: DECLARE_META_INTERFACE(SurfaceComposer); enum { // (keep in sync with Surface.java) eHidden = 0x00000004, //省略了部分内容 };//省略了部分内容}调用createConnection()接口将构建一个ISurfaceFlingerClient,而eFXSurfaceNormal eFXSurfaceBlur eFXSurfaceDim 和 eFXSurfaceMask 表示不同类型的Surface层次,它们和java代码是对应的(对应surface.java文件)SurfaceFlinger本地代码:SurfaceFlinger是Surface的本地实现。实现Surface的建立。控制,管理等功能。其路径为:frameworks/base/libs/surfaceflinger在surfaceFlinger.h和SurfaceFlinger.cpp 文件中,SurfaceFlinger类继承IsurfaceComposer,是一个核心的实现。SurfaceFlinger::BClient类继承了ISurfaceFlingerClient。另一个重要的部分就是提供不同的层(layer),用于构建不同的显示界面。在surfaceflinger内部有一个表示surface层次的类,就是LayerBase,它提供了与上层相关的通用接口,LayerBaseClient继承LayerBase,而LayerBaseClient的内部类Surface又继承Bnsurface。这个LayBaseClient是各种层的一个基类,它被以下其它几个类继承:Layer,LayerBuffer,LayerDim,和LayerBlur。这几个类则表示了几种不同的“层”,以上几个类中,LayerBuffer中的surfaceBuffer继承了本地的ISurface接口,也就是说,本地使用的ISurface接口在android的图形系统中只有LayerBuffer中的一个实现。在上层的程序调用过程中,创建一个surface过程如下: 1。调用libui接口SurfaceComposerClient::createSurface(); 2。调用ISurfaceFlingerClient::creatSurface(); 3。由于继承关系,实际上调用 的是SurfaceFlinger中的接口,即BClient::creatSurface(); 4。继续调用SurfaceFlinger::createSurface()函数,其处理过程如下: sp SurfaceFlinger::createSurface(ClientID clientId, int pid, ISurfaceFlingerClient::surface_data_t* params, DisplayID d, uint32_t w, uint32_t h, PixelFormat format, uint32_t flags){ LayerBaseClient* layer = 0; sp <:surface> surfaceHandle; Mutex::Autolock _l(mStateLock); Client* const c = mClientsMap.valueFor(clientId); if (UNLIKELY(!c)) { LOGE("createSurface() failed, client not found (id=%d)", clientId); return surfaceHandle; } //LOGD("createSurface for pid %d (%d x %d)", pid, w, h); int32_t id = c->generateId(pid); if (uint32_t(id) >= NUM_LAYERS_MAX) { LOGE("createSurface() failed, generateId = %d", id); return surfaceHandle; } switch (flags & eFXSurfaceMask) { case eFXSurfaceNormal: if (UNLIKELY(flags & ePushBuffers)) { layer = createPushBuffersSurfaceLocked(c, d, id, w, h, flags); } else { layer = createNormalSurfaceLocked(c, d, id, w, h, format, flags); } break; case eFXSurfaceBlur: layer = createBlurSurfaceLocked(c, d, id, w, h, flags); break; case eFXSurfaceDim: layer = createDimSurfaceLocked(c, d, id, w, h, flags); break; } if (layer) { setTransactionFlags(eTransactionNeeded); surfaceHandle = layer->getSurface(); if (surfaceHandle != 0) surfaceHandle->getSurfaceData(params); } return surfaceHandle;}创建surface时,调用 createSurface(),创建各个层后,分别调用不同层中的getSurface()接口来得到一个ISurface类型的实例,然后在SurfaceComposerClient::createSurface()中得到surface并将其返回。实际是根据参数flags选择使用不同的层:eFXSurfaceNormal对应的层是Layer,LayerBuffer; eFXSurfaceBlur对应的层是LayerBlur; eFXSurfaceDim对应 的层是LayerDim。如果将参数指定为普通,一般情况下会建立Layer类,当ePushBuffers为真时才会建立 LayerBuffer类。建立 Layer和LayerBuffer分别调用的是createNormalSurfaceLocked()函数和createPushBufferSurfacLocked()函数。createNormalSurfaceLocked()先要建立一个Layer类,再向其中设置一个Buffer,然后增加层。createPushBufferSurfacLocked()情况比较简单,只要建立 一个LayerBuffer的类将其加入层即可。二者的区别是:LayBuffer是一个push类型的层,通常要使用队列的方式将显示的内容“推”入其中,Layer是一个普通 的层,建立时要将一个内存设置到其中 。以设置大小 为例 ;对一个Surface进行设置的过程如下: 1,调用libui的surface接口 setSize(); 2, 实际调用 的是surfacecomposerClient::setSize(),在其中的参数surface的大小,并将其成员what设置为eSizeChanged。 3, 由于逻辑关系,由IsurfaceFlingerClient::setState()函数进行处理。 4,由于继承关系,实际调用 的是类SurfaceFlinger中的BClient::setState()。 5, 进一步调用surfaceFlinger::setClientState()函数。在代码处理中,根据不同的状态变化命令来进行处理,最终调用的是各个“层”的setSize()函数,之后的内容由几个层的不同实现来进行处理。Surfaceflinger和显示硬件的接口显示设备由DisplayHardware目录中的DisplayHardware.cpp文件来实现的,其中创建了一个DisplayHardware来作为主要的显示界面。 mDisplaySurface = new EGLDisplaySurface();EGLDisplaySurface类是在libui中实现的,它直接操作FrameBuffer的硬件驱动。在SurfaceFlinger.cpp中,将创建类DisplayHardware为实例,从而获取实际的显示设备,在上面进行显示输出。此外,surfaceflinger可以使用可选的硬件模块copybit作为2D图形处理部分的加速器。这部分的接口在frameworks/base/include/core/java/android/hardware目录的copybit.h文件定义 的相关接口。作为硬件模块使用,这个模块在DisplayHardware初始化的过程中被打开。Surface的java和JNI代码Surface部分的JNI代码路径是:frameworks/base/core/jni/android_view_Surface.cpp它主要提供了android.view.SurfaceSession和android.view.Surface两个java类,分别调用SurfaceComposerClient和Surface两个本地类来完成实现。Surface部分的Java代码的路径是:frameworks/base/core/java/android/view/由此对应的java类在android.view包中,除了上面提到的类surfaceSession和Surface之外,与其相关的还有接口SurfaceHolder和类SurfaceView。Android.view.Surface 表示一个可以绘制图形的界面,它实际上是调用 底层的Surface接口来实现控制的硬件载体。实际上,在java框架中,所有UI元素的基类都是android.view.View,这些UI元素本身也是基于Surface及2D绘图函数来实现的,如果要在java程序中使用一个可以进行*绘制的界面,那么就需要使用类android.view.SurfaceView,这个类也继承了android.view.View,因此也是android中的一个UI元素,andriod.view.SurfaceHolder是android.view.SurfaceView 中包含的一个接口,用于处理Surface相关的事件。(surface.java中定义的整数常量和本地的ISurfaceComposer.h是具有对应关系的)Skia和2D图形系统:skia是一个C++本地代码库,路径为external/skia/包含3个库 Core Cg 核心图形库: libcorecg.so GL (Skia 图形库):liblibsgl.so skia-opengl glue library : libskiagl.so核心库是libcorecg.so它是skia中最基础的库,其源代码主要在src/core/目录中,提供一系列基本功能,Skia 图形库:liblibsgl.so是skia系统主要的库,它包含移植层,图形绘制,图像编解码,效果等方面内容。其源代码主要在src/effects , src/images/ , src/ports/, src/core/ , src/utils/ 目录 中。liblibsgl.so需要连接libcorecg.so,以及被其调用 的图像的编解码库,字体处理的库等。libskiagl.so 是skia和OpenGL 相关联的库,其源代码在src/gl目录中。skia对上层有众多的接口,接口头文件在include目录 中,skia 的API中最主要的SKCavans类,这个类提供了众多的绘制功能。事实上,整个android的GUI系统的底层绘制,就是这个类来完成的。其头文件 和源代码路径分别是:include/core/SkCavans.h 和src/core/SkCavans.cppSkCanvas 类有两个构造函数,其参数类型分别是SkBitmap和SkDevice,分别表示Skia进行绘制的目标。事实上,Skia的基本功能 就是一个绘制工具,这个绘制工具和绘制的目标是无关的。SkBitmap 可以视为一个表示位图区域的内存,除了一般的内存首指针和大小 之外,还包括宽,高和像素格式等信息,在这块内存上,可以进行skia图形系统的绘制工作。SkCanvas的主要绘制功能有3种: 基本图形绘制 (如drawARGB , drawLine 函数)图像文件绘制(如drawBitmap函数)和文本绘制(如drawText函数).Skia的图像编码部分:skia的图像编码部分是一个相对独立 的部分,其接口分别在include/image目录下的SkImageDecoder.h 和skImageEncoder.h中定义:SkImageDecoder既可以作为动态类使用,又可以使用静态函数,也支持同步和异步方式解码,它可以把图像文件或者流解码到skia的内部内存SkBitmap中。SkImageEncoder和解码器类似,完成 的是编码工作,skia的解码器和编码器都是接口,需要具体的类去实现,在src/images中的几个源文件通过继承SkImageDecoder和SkImageEncoder来实现解码器和编码器,如 SkImageDecoder_libjpeg.cpp 通过调用libjpeg 库实现了JPEG的解码。如有其它 的解码器和编码器,也可以通过 SkImageEncoder和SkImageDecoder的类来实现。Android图形系统的JNI接口android图形系统和skia底层库联系比较紧密,android图形系统的JNI提供 了从skia底层到java层的支持,JNI代码路径为:frameworks/base/core/jni/android/grphic/Canvas.cpp 是JNI中的核心接口,为java上层的android.graphics.Canvas类提供了支持,其中 ,initRaster()和initGL()两个函数将和Skia本地库联系起来。即通过建立skia本地库的Cavvas来建立给java层使用的Canvas,除此外,图形JNI还通过JNI接口,提供andriod.graphics.Region , android.graphics.Bitmap, android.graphics.Picture , android.graphics.Matrix 等多个java类的支持,这一般调用的是skia中同名文件中的函数。Android的图形包:android图形类的包是android.graphics它通过调用图形系统的JNI提供了对java框架中的图形系统的支持,代码路径为:frameworks/graphic/java/android/graphics/Canvas.java定义了android图形系统中最为重要的一个类:android.graphics.Canvas. Canvas类处理“draw“的调用,当绘制(draw)内容时需要4个基本组件,一个保持像素的Bitmap,一个处理绘制调用 的canvas(写入Bitmap),绘制 的内容(如,Rect,Path,text,Bitmap)和一个paint(用开描述颜色和样式)。Bitmap.java文件实现了类android.graphics.Bitmap, 它表示内存中的一个位图。Canvas是一个比较基础的图形类,android中的UI 元素也是通过调用Canvas类来构建的。在View.java文件 中,实现的类是android.view.View, 通过建立这个Canvas类来构建绘画的基础。Android系统的OpenGL系统与3D图形系统分本地代码和java框架代码两部分。本地代码实现OpenGL接口的库,java框架层,javax.microedition.khronos.opengles是java标准的OpenGL包。本地整体结构:主要内容在frameworks/base/opengl/中,本地代码头文件 路径为: frameworks/base/opengl/include/EGL/ frameworks/base/opengl/include/GLES/源代码目录: frameworks/base/opengl/libagl/ frameworks/base/opengl/libs/编绎后生成3个库: libGLESv1_CM.so: OpenGL Es库的封装,对应libs/GLES_CM目录 中的文件 libEGL.so:EGL库,OpenGL Es库的封装,对应libs/EGL目录 中的文件 libagl.so: OpenGL的软件实现库,对应libagl目录中的文件。Android的OpenGL实现方式:OpenGL本地库可使用软件库和硬件库这两种不同的 方式来实现。如果是软件实现,则用libagl.so库,如果是硬件实现,则使用libhgl.so库。在libs/EGL/egl.cpp文件中,选择使用这两个库中的一个,主要操作在eglGetDisplay()函数中实现。load_driver()函数实际上将通过dlopen()方式打开库,默认先去找OpenGL的软件实现libagl.so 当设置使用OpenGL的硬件实现(在init.rc中设置属性debug.egl.hw)时,则使用OpenGL的硬件实现libagl.so.然后用dlsysm方式打开其中支持API的符号,gl_hooks_t 结构描述了OpenGL系统所支持的各种API。实际的符号将在gl_entries.in和egl_entries.in两个文件中定义。各种函数以GL_ENTRY和EGL_ENTRY的方式来进行描述。Android的openGL的本地测试代码:路径为:frameworks/base/opengl/tests 包含几个独立 的测试程序,openGL的JNI代码:openGL向上提供的JNI接口,主要由以下两个文件提供: frameworks/base/core/jni/com_google_android_gles_jni_GLImpl.cpp frameworks/base/core/jni/com_google_android_gles_jni_EGLImpl.cpp这是包com.google.android.gles_jni中的两个类,分别是EGLImpl和GLImpl,分别对应egl和gl的实现。其中,EGLImpl中的各个接口负责一些管理功能,而GLImpl中的各个接口对应于OpenGL的GLES/gl.h头文件中定义的各个OpenGL功能函数。OpenGL的java类在andriod中使用openGL常要结合使用javax.microedition.khronos.opengles 和android.opengl包。实现方法是使用一个类来继承OpenGL java的标准类,通过实现这个类来实现,在java层只要使用标准类。OpenGL 的java类是javax中的一部分,这个包是javax.microedition.khronos.egl 和javax.microedition.khronos.opengls路径为: opengl/java/javax/microedition/khronos/egl/ opengl/java/javax/microedition/khronos/opengles/在egl类中主要的文件是GL10.java 和GL11.java ;在opengles中主要的文件 是EGL10.java;android 使用继承的方法实现opengl这个继承的包为com.google.android.gles_jni 其路径为: opengl/java/com/google/andriod/gles_jni/事实上,EGLImpl和GLImpl的大部分函数是通过JNI调用本地的OpenGL程序来实现的。在java应用层不会调com.google.android.gles_jni包中的类,只会调用标准包avax.microedition.khronos.opengles中的接口。另个,android.opengl包提供了openGL的标准接口到android系统的媒介,路径为: opengl/java/android/opengl/其中主要的类调用com.google.android.gles_jni包中的类和android的基础GUI系统的类实现GLSurfaceViewGLSurfaceView继承了SurfaceView。 surfaceView又继承View,因此GLSurfaceView本身也是个UI元素,在android的java应用程序层使用OpenGL,具体的实现其实是继承GLSurfaceView类并调用OpenGL标准的接口。