surface
-
2023年4月28日发(作者:心灰意冷的意思)【转】android中的surface
默认分类 2010-07-30 10:03:03 阅读780 评论0 字号:大中小
在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
- unlockAndPost
frameworks/base/core/java/android/view/ —
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_init ()。
在这个函数中SurfaceComposerClient 对象被创建。
frameworks/base/libs/ui/
—SurfaceComposerClient::SurfaceComposerClient ()这个函数非常
重要,在这里建立了client和server之间的桥梁。通过函数
_get_surface_manager()获得了一个指向server的IBinder 对象(具
有ISurfaceComposer接口),之后通过这个IBinder就可以跨进程访问
Server的功能。接着调用 ISurfaceComposer::createConnection()创
建并返回了一个ISurfaceFlingerClient的 IBinder。
frameworks/base/libs/ui/
—SurfaceComposerClient::createSurface().这个函数中,利用前面
获得的ISurfaceFlingerClient的IBinder,调用其createSurface接
frameworks/base/libs/surfaceflinger/ —
BClient::createSurface ()。Bclient由ISurfaceFlingerClient派生
而来
frameworks/base/libs/surfaceflinger/ —
SurfaceFlinger:: createSurface()。这个函数为Surface创建一个对
应的Layer。
Android GUI系统:
涉及JAVA框架层的内容:
cs 类 ,对应Skia底层库,提供绘图接口。
e 构建显示界面。
各种UI元素的基类。
es 标准的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.h
Point.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,它是最终的实现类。
在所实现的构造函数中调用
mapFrameBuffer()函数对驱动程序进行操作如:
status_t EGLDisplaySurface::mapFrameBuffer()
{
char const * const device_template[] = {
"/dev/graphics/fb%u",
"/dev/fb%u",
0 };
while ((fd==-1) && device_template[i]) {
snprintf(name, 64, device_template[i], 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, _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, _len);
//省略了部分内容
}
2.输入的硬件接口:
对andriod的事件处理部分,主要是向上层提供统一的按键码
(KeyCode),是一个整数,上层的Java程序中主要通过这个值来判断
系统的实现。在libui中通过对标准的Input驱动的处理来将input值
转换成andriod系统的按键码,按键码参考KeyCharacterMay.h头文件。
文件是输入部分的硬件抽象定义设备节点所在的路
径。
Static const char *device_path = “/dev/input”; //输入
设备目录
处理过程中将搜索路径下面所有input驱动设备节点,这在
openPlatformInput()中通过调用scan_dir()来实现,scan_dir()将会
从目录中查找设备,找到后调用open_device()将其打开。
bool EventHub::openPlatformInput(void)
{
//省略了部分内容
res = scan_dir(device_path);
if(res < 0) {
LOGE("scan dir failed for %sn", 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[i].revents) {
if(mFDs[i].revents & POLLIN) {
res = read(mFDs[i].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表示的数值和
nt类中数值是一致的,这个java类的路径为
frameworks/base/core/java/android/view/)
开发过程中对不同的硬件,只要定不同的键盘布局文件就OK了。
Android中一般不需要增加新的按键码。
(在andriod的输入处理经过了两次映射,第一次将Event驱动中的整
数按键码映射成字符串,第二次将字符串映射成Java的UI 程序中使用
的整数值。如要增加按键在用户程序 中进行处理除了
KeyCharacterMap.h和两个文件 ,还要改两个文件
tools/puppet_master/_和
frameworks/base/core/res/res/values/
Surface系统:
包括本地代码和java代码部分,关系如下:
libui提供本地的Surface系统框架
surfaceflinger完成本地接口实现
java框架层次主要调用Surface向UI 提供接口
本地部分可以使用ISurface接口。
Surface系统本地接口
在libui中定义Surface的本地接口,路径
frameworks/base/include/ui,主要有以下几个文件;
Surface.h
SurfaceComposerClient.h
ISurface.h
ISurfaceFlingerClient.h
IsurfaceComposer.h
Surface.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 )
eHidden = 0x00000004,
//省略了部分内容
};
//省略了部分内容
}
调用createConnection()接口将构建一个ISurfaceFlingerClient,而
eFXSurfaceNormal eFXSurfaceBlur eFXSurfaceDim 和
eFXSurfaceMask 表示不同类型的Surface层次,它们和java代码是对
应的(对应文件)
SurfaceFlinger本地代码:
SurfaceFlinger是Surface的本地实现。实现Surface的建立。控制,
管理等功能。其路径为:
frameworks/base/libs/surfaceflinger
在surfaceFlinger.h和 文件中,
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 surfaceHandle;
Mutex::Autolock _l(mStateLock);
Client* const c = or(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来作为主要的显示界面。
mDisplaySurface = new EGLDisplaySurface();
EGLDisplaySurface类是在libui中实现的,它直接操作FrameBuffer
的硬件驱动。在中,将创建类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_
它主要提供了eSession和
e两个java类,分别调用SurfaceComposerClient
和Surface两个本地类来完成实现。
Surface部分的Java代码的路径是:
frameworks/base/core/java/android/view/
由此对应的java类在包中,除了上面提到的类
surfaceSession和Surface之外,与其相关的还有接口SurfaceHolder
和类SurfaceView。
e 表示一个可以绘制图形的界面,它实际上是调
用 底层的Surface接口来实现控制的硬件载体。
实际上,在java框架中,所有UI元素的基类都是,
这些UI元素本身也是基于Surface及2D绘图函数来实现的,如果要在
java程序中使用一个可以进行自由绘制的界面,那么就需要使用类
eView,这个类也继承了,因此
也是android中的一个UI元素,eHolder是
eView 中包含的一个接口,用于处理Surface相
关的事件。
(中定义的整数常量和本地的ISurfaceComposer.h是具
有对应关系的)
Skia和2D图形系统:
skia是一个C++本地代码库,路径为external/skia/
包含3个库
Core Cg 核心图形库:
GL (Skia 图形库):
skia-opengl glue library :
核心库是它是skia中最基础的库,其源代码主要在
src/core/目录中,提供一系列基本功能,
Skia 图形库:是skia系统主要的库,它包含移植层,图
形绘制,图像编解码,效果等方面内容。其源代码主要在src/effects ,
src/images/ , src/ports/, src/core/ , src/utils/ 目录 中。
需要连接,以及被其调用 的图像的编解码
库,字体处理的库等。
是skia和OpenGL 相关联的库,其源代码在src/gl目录
中。
skia对上层有众多的接口,接口头文件在include目录 中,skia 的
API中最主要的SKCavans类,这个类提供了众多的绘制功能。事实上,
整个android的GUI系统的底层绘制,就是这个类来完成的。其头文件
和源代码路径分别是:include/core/SkCavans.h 和
src/core/
SkCanvas 类有两个构造函数,其参数类型分别是SkBitmap和
SkDevice,分别表示Skia进行绘制的目标。事实上,Skia的基本功能 就
是一个绘制工具,这个绘制工具和绘制的目标是无关的。SkBitmap可以
视为一个表示位图区域的内存,除了一般的内存首指针和大小 之外,
还包括宽,高和像素格式等信息,在这块内存上,可以进行skia图形
系统的绘制工作。
SkCanvas的主要绘制功能有3种: 基本图形绘制 (如drawARGB ,
drawLine 函数)图像文件绘制(如drawBitmap函数)和文本绘制(如
drawText函数).
Skia的图像编码部分:
skia的图像编码部分是一个相对独立 的部分,其接口分别在
include/image目录下的SkImageDecoder.h 和skImageEnc
oder.h中定义:
SkImageDecoder既可以作为动态类使用,又可以使用静态函数,也支持
同步和异步方式解码,它可以把图像文件或者流解码到skia的内部内
存SkBitmap中。
SkImageEncoder和解码器类似,完成 的是编码工作,
skia的解码器和编码器都是接口,需要具体的类去实现,在src/images
中的几个源文件通过继承SkImageDecoder和SkImageEncoder来实现解
码器和编码器,如 SkImageDecoder_ 通过调用libjpeg 库
实现了JPEG的解码。
如有其它 的解码器和编码器,也可以通过 SkImageEncoder和
SkImageDecoder的类来实现。
Android图形系统的JNI接口
android图形系统和skia底层库联系比较紧密,android图形系统的JNI
提供 了从skia底层到java层的支持,JNI代码路径为:
frameworks/base/core/jni/android/grphic/
是JNI中的核心接口,为java上层的
类提供了支持,其中 ,initRaster()和
initGL()两个函数将和Skia本地库联系起来。即通过建立skia本地
库的Cavvas来建立给java层使用的Canvas,除此外,图形JNI还通过
JNI接口,提供 , ,
e , 等多个java
类的支持,这一般调用的是skia中同名文件中的函数。
Android的图形包:
android图形类的包是cs它通过调用图形系统的JNI
提供了对java框架中的图形系统的支持,代码路径为:
frameworks/graphic/java/android/graphics/
定义了android图形系统中最为重要的一个类:
. Canvas类处理“draw“的调用,当绘制
(draw)内容时需要4个基本组件,一个保持像素的Bitmap,一个处理
绘制调用 的canvas(写入Bitmap),绘制 的内容(如,Rect,Path,
text,Bitmap)和一个paint(用开描述颜色和样式)。文
件实现了类, 它表示内存中的一个位图。
Canvas是一个比较基础的图形类,android中的UI 元素也是通过调用
Canvas类来构建的。在文件 中,实现的类是
, 通过建立这个Canvas类来构建绘画的基础。
Android系统的OpenGL系统与3D图形系统
分本地代码和java框架代码两部分。
本地代码实现OpenGL接口的库,java框架层,
es是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_: OpenGL Es库的封装,对应libs/GLES_CM目录
中的文件
:EGL库,OpenGL Es库的封装,对应libs/EGL目录 中
的文件
: OpenGL的软件实现库,对应libagl目录中的文件。
Android的OpenGL实现方式:
OpenGL本地库可使用软件库和硬件库这两种不同的 方式来实现。如果
是软件实现,则用库,如果是硬件实现,则使用
库。
在libs/EGL/文件中,选择使用这两个库中的一个,主要操作
在eglGetDisplay()函数中实现。
load_driver()函数实际上将通过dlopen()方式打开库,默认先去找
OpenGL的软件实现当设置使用OpenGL的硬件实现(在
中设置属性)时,则使用OpenGL的硬件实现
.然后用dlsysm方式打开其中支持API的符号,
gl_hooks_t 结构描述了OpenGL系统所支持的各种API。实际的符号将
在gl_和egl_两个文件中定义。各种函数以
GL_ENTRY和EGL_ENTRY的方式来进行描述。
Android的openGL的本地测试代码:
路径为:frameworks/base/opengl/tests 包含几个独立 的测试程序,
openGL的JNI代码:
openGL向上提供的JNI接口,主要由以下两个文件提供:
frameworks/base/core/jni/com_google_android_gles_jni_GLImpl.c
pp
frameworks/base/core/jni/com_google_android_gles_jni_EGLImpl.
cpp
这是包_jni中的两个类,分别是EGLImpl和
GLImpl,分别对应egl和gl的实现。
其中,EGLImpl中的各个接口负责一些管理功能,而GLImpl中的各个接
口对应于OpenGL的GLES/gl.h头文件中定义的各个OpenGL功能函数。
OpenGL的java类
在andriod中使用openGL常要结合使用
es和包。
实现方法是使用一个类来继承OpenGL java的标准类,通过实现这个类
来实现,在java层只要使用标准类。
OpenGL 的java类是javax中的一部分,这个包是
和
s路径为:
opengl/java/javax/microedition/khronos/egl/
opengl/java/javax/microedition/khronos/opengles/
在egl类中主要的文件是 和 ;在opengles中主
要的文件 是;
android 使用继承的方法实现opengl这个继承的包为
_jni 其路径为:
opengl/java/com/google/andriod/gles_jni/
事实上,EGLImpl和GLImpl的大部分函数是通过JNI调用本地的OpenGL
程序来实现的。
在java应用层不会调_jni包中的类,只会
调用标准包es中的接口。
另个,包提供了openGL的标准接口到android系统的
媒介,路径为:
opengl/java/android/opengl/
其中主要的类调用_jni包中的类和android
的基础GUI系统的类实现GLSurfaceView
GLSurfaceView继承了SurfaceView。 surfaceView又继承View,因此
GLSurfaceView本身也是个UI元素,在android的java应用程序层使
用OpenGL,具体的实现其实是继承GLSurfaceView类并调用OpenGL标
准的接口。
-
windows平板电脑