Android 的 GUI 系统
Android 的 GUI 系统
第一部分 Android GUI 系统综述
第二部分 pixelflinger 和 libui 库
第三部分 Surface 系统
第五部分 Skia 系统
第六部分 OpenGL 系统架构
第一部分 Android GUI 系统综述
libpixelflinger
SurfaceFlinger
libui
SurfaceOverlay Camera EglWindowsformat Key / Event
Skia图形引擎 OpenGL3D引擎
Graphic JNISurface JNI
GLSurfaceView 各种GUI元素
OpenGL JNI
_jni
FrameBuffer
驱动
C框架层
JAVA框架
Event输入
驱动
第一部分 Android GUI 系统综述
Android的 GUI系统由 C语言的框
架和 JAVA语言的框架组成。
GUI系统的 C语言部分包括:
PixelFlinger
libui(框架库)
SurfaceFlinger( Surface的管
理)
Skia图形图像引擎
OpenGL 3D 引擎
各种 JNI(向 JAVA提供接口)
第一部分 Android GUI 系统综述
GUI系统 JAVA语言的核心包括:
(对应 Skia底层库)
(构建显示介
面)
及其继承者
(用于构建 UI元素)
OpenGL的功能类
(由 _jni
实现)
第二部分 pixelflinger 和 libui 库
pixelflinger
libui
pixelflinger
是一个下层的工具性的类,这个
类对外的主要内容是 GGLContext 结构,以及初始化和
卸载的函数。
system/core/include/pixelflinger/
system/core/libpixelflinger/
ssize_t gglInit(GGLContext** context);
ssize_t gglUninit(GGLContext* context);
这个库对其他的库没有依赖,也
并不提供实际的功能,类似一个用于管理工具的库。
libui
libui 是一个框架性质的集成库,它不仅是显示的
中枢,也是整个 GUI 系统的中枢。
UI lib ( libui → libpixelflinger ) ,这个的相关内
容在以下的路径中:
frameworks/base/include/ui/
framework/base/libs/ui/
libui 包含了颜色格式,用于实际显示的 Egl 窗
口,按键及事件处理, Suface 界
面, Overlay , Camera 等几个方面的接口。
libui
format 部分:
这个部分本身定义颜色空间的枚举和数据结构,它需
要充用 pixelflinger 中的一些关于数据格式定义。
EglWindows 部分:
包含了 EGL 头文件构建的 egl_native_window_t ,它依
赖 OpenGL 的结构,并给 libEGL 使用的。 EGLDisplaySurface
操作了硬件的 framebuffer 的驱动。这也是整个系统显示的基
础。
Key/Event 部分:
这是 Android 系统输入的基础,其中定义按键的映射,通
过操作 event 事件设备来实现获取系统的输入的。
libui
Surface :
Surface 相关的头文件和实现为 SurfaceFlinger
定义接口和框架。
Overlay :
定义视频输出的接口。
Camera :
定义摄像头的框架和接口。
输出部分的硬件抽象( donut 之前):
调用标准的 FrameBuffer 驱动。
Android 的显示输出系统
Android 使用标准的 framebuffer 作为驱动程
序, Android 的本地框架中提供了系统和
framebuffer 驱动程序之间的适配层(硬件抽象
层)。
Gralloc Module是 Eclair版本之后显示
部分的抽象层,它是系统和 Framebuffer设备
的接口,以硬件模块的形式存在。
头文件路径:
hardware/libhardware/include/hardware/
Gralloc模块实现:
hardware/libhardware/modules/gralloc/
Gralloc被 libui使用。
Android 的显示输出系统
中包含了 Gralloc模块需要具
有的接口。
typedef struct gralloc_module_t {
struct hw_module_t common;
int (*registerBuffer)(struct gralloc_module_t const* module,
buffer_handle_t handle);
int (*unregisterBuffer)(struct gralloc_module_t const* module,
buffer_handle_t handle);
int (*lock)(struct gralloc_module_t const* module,
buffer_handle_t handle, int usage,
int l, int t, int w, int h,
void** vaddr);
int (*unlock)(struct gralloc_module_t const* module,
buffer_handle_t handle);
int (*perform)(struct gralloc_module_t const* module,
int operation, ... );
void* reserved_proc[7];
} gralloc_module_t;
Android 的显示输出系统
enum {
GRALLOC_USAGE_SW_READ_NEVER = 0x00000000,
GRALLOC_USAGE_SW_READ_RARELY = 0x00000002,
GRALLOC_USAGE_SW_READ_OFTEN = 0x00000003,
GRALLOC_USAGE_SW_READ_MASK = 0x0000000F,
GRALLOC_USAGE_SW_WRITE_NEVER = 0x00000000,
GRALLOC_USAGE_SW_WRITE_RARELY = 0x00000020,
GRALLOC_USAGE_SW_WRITE_OFTEN = 0x00000030,
GRALLOC_USAGE_SW_WRITE_MASK = 0x000000F0,
/* buffer will be used as an OpenGL ES texture */
GRALLOC_USAGE_HW_TEXTURE = 0x00000100,
/* buffer will be used as an OpenGL ES render target */
GRALLOC_USAGE_HW_RENDER = 0x00000200,
/* buffer will be used by the 2D hardware blitter */
GRALLOC_USAGE_HW_2D = 0x00000C00,
/* buffer will be used with the framebuffer device */
GRALLOC_USAGE_HW_FB = 0x00001000,
/* mask for the software usage bit-mask */
GRALLOC_USAGE_HW_MASK = 0x00001F00,
};
Android 的显示输出系统
Gralloc模块是显示模块的实现。其中,
用于基于 framebuffer的显示
实现, gralloc是基于 pmem的实现。
int gralloc_device_open(const hw_module_t* module, const char* name,
hw_device_t** device)
{
int status = -EINVAL;
if (!strcmp(name, GRALLOC_HARDWARE_GPU0)) {
gralloc_context_t *dev;
dev = (gralloc_context_t*)malloc(sizeof(*dev));
memset(dev, 0, sizeof(*dev));
dev-> = HARDWARE_DEVICE_TAG;
dev-> = 0;
dev-> = const_cast<hw_module_t*>(module);
dev-> = gralloc_close;
dev-> = gralloc_alloc;
dev-> = gralloc_free;
*device = &dev->;
status = 0;
} else {
status = fb_device_open(module, name, device);
}
return status;
}
Android 的显示输出系统
libui对 gralloc模块实现了调用,在
ui/中打开
了 gralloc设备。
FramebufferNativeWindow::FramebufferNativeWindow()
: BASE(), fbDev(0), grDev(0), mUpdateOnDemand(false)
{
hw_module_t const* module;
if (hw_get_module(GRALLOC_HARDWARE_MODULE_ID, &module) == 0) {
int stride;
int err;
err = framebuffer_open(module, &fbDev);
LOGE_IF(err, "couldn't open framebuffer HAL (%s)", strerror(-err));
err = gralloc_open(module, &grDev);
LOGE_IF(err, "couldn't open gralloc HAL (%s)", strerror(-err));
// ......
}
Android 的显示输出系统
Android 的用户输入系统
Android输入系统由 Event 驱动程
序, libui中的 EventHub和 JAVA框架中
的几个类组成。
Event的功能被集成在
包的 View类中,在应用程序层调用主要通过
View类及其继承者。
KeyEvent和 MotionEvent的处理方法
略有不同, KeyEvent通过转化按键码得
到, MotionEvent通过转化 RawEvent得
到。
Android 的用户输入系统
libui
EventHub
Java
Framework
Java
Application
Native
Framework
/dev/input/
Kernel
RawInputEventKeyInputQueue
*.kl
KeyInputDevice
MotionEvent
RawInputEvent
KeyEvent
KeyEvent
WindowManagerService
Service
onKeyDown
onKeyUp
onTouchEvent
onTrackballEvent
transfer
Touch / Mouse / Key
KeyLayoutMap
KeyCharacterMap
*.kcm
输入部分的硬件抽象:
调用标准的 Event 设备驱动。
<=> android/view/
键盘布局文件
Android 的用户输入系统
Multi-Touch 是 Eclair版本的新特
性。
多点触摸的特性,需要从硬件到软件系统
的支持。作为 Android的 GUI系统,最终的就
是将消息从下传递到上面。
输入设备中增加了一个新的类型:
TOUCHSCREEN_MT( 定义),
EventHub获得信息只有交由 JAVA层处理。
Android 的用户输入系统
增加
多点数据表示, InputDevice作处理,并保
持对非多点触摸的兼容性, KeyInputQueue
( android/server/KeyInputQueue)
进行多点数据的转化。
MotionEvent增加了对多点的支持,
最多支持同时有 3个点触摸。这是上层程序中
用到的接口。
注意:没有手势方面的解析,需要应用
程序根据多点信息自己实现解释。
Android 的用户输入系统
Virtual Key 是
Eclair版本的新特性。
Virtual Key的功能是利
用触摸屏,模拟按键发生的事
件,这样就可以利用触摸屏的边
缘,实现一些可以自定义的按键
效果。
A B C
D
E
F
G
H
Android 的用户输入系统
从应用程序的角度,触摸屏设备发送的是
RawInputEvent(原始输入事件),而按键发
送的是 KeyEvent(按键事件)。虚拟按键的作
用是在某种情况下,将 RawInputEvent转换成
KeyEvent。
按键文件:
/sys/board_properties/virtualkeys.{devicename}
代码路径:
services/java/com/android/server/
通过 readVirtualKeys,进行消息的转
化,将 RawInputEvent转换成 KeyEvent。
Android 的用户输入系统
virtualkeys.{devicename}是虚拟按键
的适配文件,需要放置在目标系统的以下目录中
: /sys/board_properties/
Android 的用户输入系统
文件的格式如下所示:
0x1:扫描码 :X:Y:W:H:0x1: ……
第三部分 Surface 系统
Surface 系统的结构
SurfaceFlinger 本地代码
Surface 的 Java 和 Jni代码
Surface 系统结构
Surface 系统的结构:
提供与 Surface 接口。
提供实现。
Java 框架层次主要调用 Surface
向 UI 提供接口。
Navtive (本地调用)部分主要使
用 ISurface 。
Surface 系统的结构
+registerBuffers()
+postBuffer()
+unregisterBuffers()
ISurface
BnSurface
+ID()
+setLayer()
+setPosition()
+setSize()
+setAlpha()
+getIdentity()
+dirtyRegion()
+setDirtyRegion()
-mClient
-mSurface
Surface
+createSurface()
+destroySurface()
ISurfaceFlingerClient
BnSurfaceFlingerClient
+createConnection()
+freezeDisplay()
+unfreezeDisplay()
+setOrientation()
+bootFinished ()
+requestGPU()
+revokeGPU()
ISurfaceComposer
BnSurfaceComposer
+createSurface()
-ISurfaceFlingerClient
SurfaceComposerClient
Surface 系统的结构
Surface系统的头文件
(路径为: frameworks/base/include/ui/) :
Surface 系统的源代码文件
(路径为: frameworks/base/libs/surfaceflinger/) :
SurfaceFlinger类继承了 ISurfaceComposer ,是一个核心的实现。
Surface 系统的结构
class ISurfaceComposer : public IInterface
{
public:
enum { // (keep in sync with )
eHidden = 0x00000004,
eGPU = 0x00000008,
eHardware = 0x00000010,
eDestroyBackbuffer = 0x00000020,
eSecure = 0x00000080,
eNonPremultiplied = 0x00000100,
ePushBuffers = 0x00000200,
eFXSurfaceNormal = 0x00000000,
eFXSurfaceBlur = 0x00010000,
eFXSurfaceDim = 0x00020000,
eFXSurfaceMask = 0x000F0000, };
enum {
ePositionChanged = 0x00000001,
/* ... ... */ };
enum {
eLayerHidden = 0x01,
/* ... ... */ };
enum {
eOrientationDefault = 0,
/* ... ... */ };
}
SurfaceFlinger 本地代码
代码的路径 :
frameworks/base/libs/surfaceflinger/
class LayerBaseClient : public LayerBase
class Surface : public BnSurface
class Layer : public LayerBaseClient
class LayerBuffer : public LayerBaseClient
class SurfaceBuffer : public LayerBaseClient::Surface
class LayerDim : public LayerBaseClient
class LayerBlur : public LayerBaseClient
SurfaceFlinger 本地代码
+createSurface()
+destroySurface()
-mId
-mFlinger
BClient
+createConnection()
+freezeDisplay()
+unfreezeDisplay()
+setOrientation()
+bootFinished()
+requestGPU()
+revokeGPU()
SurfaceFlinger
+setPosition()
+setLayer()
+setSize()
+setAlpha()
+getTypeID()
+getTypeInfo()
LayerBase
+getSurface()
-client
-clientIndex
-getIdentity
LayerBaseClient
+getSurfaceData()
+registerBuffers()
+postBuffer()
+unregisterBuffers()
LayerBaseClient::Surface
LayerBuffer
+getSurfaceData()
+registerBuffers()
+postBuffer()
+unregisterBuffers()
LayerBuffer::SurfaceBuffer
LayerBlur
+getLayerUser()
+getLayers()
+getSurfaceHeapManager()
Client
+registerBuffers()
+postBuffer()
+unregisterBuffers()
ISurface
BnSurface
+ID()
+setLayer()
+setPosition()
+setSize()
+setAlpha()
+getIdentity()
+dirtyRegion()
+setDirtyRegion()
-mClient
-mSurface
Surface
+createSurface()
+destroySurface()
ISurfaceFlingerClient
BnSurfaceFlingerClient
+createConnection()
+freezeDisplay()
+unfreezeDisplay()
+setOrientation()
+bootFinished()
+requestGPU()
+revokeGPU()
ISurfaceComposer
BnSurfaceComposer
+createSurface()
-ISurfaceFlingerClient
SurfaceComposerClient
LayerDim Layer
libui
SurfaceFlinger
SurfaceFlinger 本地代码
For Create a Surface:
SurfaceComposerClient::createSurface
(ISurface → Suface,
frameworks/base/libs/ui/)
→ (IsurfaceFlingerClient:: createSurface)
→ Bclient::createSurface
(framework/base/libs/surfaceflinger/)
→ SurfaceFlinger::createSurface
(framework/base/libs/surfaceflinger/)
LayerBaseClient* layer = 0;
sp<LayerBaseClient::Surface> surfaceHandle;
eFXSurfaceNormal → Layer or LayerBuffer
eFXSurfaceBlur → LayerBlur
eFXSurfaceDim → LayerDim
surfaceHandle = layer->getSurface();
For setSize:
Surface::setSize()
→ SurfaceComposerClient::setSize()
Surface 的 Java 和 Jni代码
SurfaceSurfaceComposerClient
android/view/Surfaceandroid/view/SurfaceSession
+setLayer()
+setPosition()
+setPosition()
+getHolder()
-mSurface
-mSurfaceHolder
+getSurface()
+lockCanvas()
+setFixedSize()
+setFormat()
+setKeepScreenOn()
+setSizeFromLayout()
+setType()
+unlockCanvasAndPost()
<<接口>>
::SurfaceHolder
JNI
JAVA
Framework
JAVA
Application
z
Surface 的 Java 和 Jni代码
#include <ui/>
const char* const kSurfaceSessionClassPathName
= "android/view/SurfaceSession";
const char* const kSurfaceClassPathName
= "android/view/Surface";
static void nativeClassInit(JNIEnv* env, jclass clazz);
JNI Code path:
frameworks/base/core/jni/
Surface 的 Java 和 Jni代码
#include <ui/>
const char* const kSurfaceSessionClassPathName
= "android/view/SurfaceSession";
const char* const kSurfaceClassPathName
= "android/view/Surface";
static void nativeClassInit(JNIEnv* env, jclass clazz);
JNI 代码的路径 :
frameworks/base/core/jni/
Surface 的 Java 和 Jni代码
Java 代码的路径 :
frameworks/base/core/java/android/view/
3 个主要的类 :
Surface ()
SurfaceView ()
SurfaceSession ()
1 个接口 :
SurfaceHolder ()
表示了一个可以绘制图形的界
面,它是实际调用底层的 Suface 接口来完成来控制的硬
件载体。
Surface 的 Java 和 Jni代码
关于 类:
View 类呈现了最基本的 UI 构造块。一个视图占据
屏幕上的一个方形区域,并且负责绘制和时间处理。
View 是 widgets 的基类,常用来创建交互式的用户图形
界面 (GUI) 。
View 中包含了一个 Surface ,并处理
onDraw(Canvas) 事件。
第四部分 Skia 及 android 的图形系统
Skia 底层库
Android 图形系统的 JNI 接口
Android 的图形类
第四部分 Skia 及 android 的图形系
统
skia-opengl glue library
()
SGL(Skia Graphic lib)
()
Core Cg
()
libjpg libgiflibpng libft2
Codec
Plugin
Skia
Codec
Skia Android Porting
Android basic lib
Skia API
Canvas .*
C框架层
Java框架
Graphics JNI
Skia 底层库
Skia 是 Google 一个底层的图形,图像,动
画, SVG ,文本等多方面的图形库,它是
Android 中图形系统的引擎。
Skia代码的路径:
external/skia/
Skia 主要包含三个库:
Core Cg :
GL ( Skia Graphic Lib ):
skia-opengl glue library :
Skia 底层库
Skia 主要包含三个库的代码路径:
核心图形库:
src/core/
Skia 图形库:
src/effects/
src/images/
src/ports/
src/core/
src/utils/
skia-opengl glue library :
src/gl/
Android 图形系统的 JNI 接口
Android 的图形系统和 Skia 底层库的
联系比较紧密。 Android 的图形系统的
JNI 提供了从 Skia 底层库到 JAVA 上层的
支持。
Android 的图形系统的 JNI代码的路
径:
frameworks/base/core/jni/android/graphic/
Android 的图形系统对 JAVA 层提供了
绘制基本图形的功能,是 GUI 系统的基
础。
Android 图形系统的 JNI 接口
是 JNI 中核心的接口,为
JAVA上层的 类
提供了支持。
static SkCanvas* initRaster(JNIEnv* env, jobject, SkBitmap* bitmap) {
return bitmap ? new SkCanvas(*bitmap) : new SkCanvas;
}
static SkCanvas* initGL(JNIEnv* env, jobject) {
return new SkGLCanvas;
}
Android 的图形类
Android 的图形类的包是 ,
它通过调用图形系统的 JNI 提供了对 JAVA 框架
中图形系统的支持。
Android 的图形系统的代码的路径:
frameworks/graphic/java/android/graphic/
Android 的图形类
实现图形系统中最为重要的一
个类: 。
Canvas 类处理“ draw” 的调用,当绘制
( draw )内容的时候需要 4 个基本的组件:一
个保持像素的 A Bitmap ,一个处理绘制调用的
Canvas (写入 bitmap ), 绘制的内容(例如
Rect , Path , text , Bitmap )和一个 paint
(用来描述颜色和样式)。
第五部分 OpenGL 系统
OpenGL系统结构
OpenGL的 Native代码
OpenGL的 JAVA和 JNI代码
OpenGL系统结构
libEGL
libGLESv1_CM
libagl
libhgl
Software
OpenGL
Hardware
OpenGL
_jni
Java
Framework
Native
Framework
egl_native_window_t
Surface
系统
OpenGL引擎
OpenGL JNI
OpenGL的 Native代码
OpenGL的本地代码 :
frameworks/base/opengl/libagl/
frameworks/base/opengl/libs/
OpenGL的本地头文件 :
frameworks/base/opengl/include/EGL/
frameworks/base/opengl/include/GLES/
OpenGL的库 :
( EGL 库)
( OpenGL ES 库的封装)
( OpenGL 的软件实现库)
OpenGL的 Native代码
测试代码的路径 :
frameworks/base/opengl/tests
使用 Surface的代码 :
frameworks/base/libs/surfaceflinger/
DisplayHardware/
另外的一个本地的接口 :
frameworks/base/opengl/include/EGL/
其实现:
frameworks/base/libs/ui/
struct egl_native_window_t* android_createDisplaySurface();
egl_native_window_t* android_createDisplaySurface();
Android Eclair中版本之后 libagl的库名称为
,放置到目标系统的目录
system/lib/egl。
新增 OpenGL ES2的支持:
frameworks/base/opengl/libs/ELGS_CM
库的名称为 ,它与
是并列关系。
OpenGL 的 Native代码
Android Eclair中版本的 将从
system/lib/egl目录中加载 OpenGL的实现库,加载
的方式由其中的 文件来决定, OpenGL的软件
实现 ,通常作为加载的一个库。
另外还可以选择加载硬件实现的 OpenGL库。
文件的实例如下:
0 0 android
0 1 XXX
OpenGL 的 Native代码
OpenGL的 Native代码
#include <EGL/>
#include <GLES/>
#include <GLES/>
int main(int argc, char** argv)
{
EGLint s_configAttribs[] = {
EGL_RED_SIZE, 5, EGL_GREEN_SIZE, 6, EGL_BLUE_SIZE, 5, EGL_NONE
};
EGLint numConfigs = -1; EGLint majorVersion; EGLint minorVersion;
EGLConfig config; EGLContext context;
EGLSurface surface; EGLint w, h;
EGLDisplay dpy;
dpy = eglGetDisplay(EGL_DEFAULT_DISPLAY);
eglInitialize(dpy, &majorVersion, &minorVersion);
eglChooseConfig(dpy, s_configAttribs, &config, 1, &numConfigs);
surface = eglCreateWindowSurface(dpy, config,
android_createDisplaySurface(), NULL);
context = eglCreateContext(dpy, config, NULL, NULL);
eglMakeCurrent(dpy, surface, surface, context);
eglQuerySurface(dpy, surface, EGL_WIDTH, &w);
eglQuerySurface(dpy, surface, EGL_HEIGHT, &h);
GLint dim = w<h ? w : h;
OpenGL的 Native代码
glBindTexture(GL_TEXTURE_2D, 0);
glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexEnvx(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
glEnable(GL_TEXTURE_2D);
glColor4f(1,1,1,1);
glDisable(GL_DITHER);
glShadeModel(GL_FLAT);
/* ...... */
return 0;
}
OpenGL的 JAVA和 JNI代码
( JAVA标准类) :
opengl/java/javax/microedition/khronos/egl/
( )
opengl/java/javax/microedition/khronos/opengles/
()
_jni (Android的 GLES的实现 )
opengl/java/com/google/android/gles_jni/
_jni JNI:
core/jni/
core/jni/
Android的 OpenGL的实现方法是使用一
个类来继承 OpenGL JAVA的标准类,通过对这
个类的实现,实现 OpenGL的功能,在 JAVA层
只需要使用标准类。
OpenGL的 JAVA和 JNI代码
JAVA代码:
( JAVA标准类) :
opengl/java/javax/microedition/khronos/egl/
( )
opengl/java/javax/microedition/khronos/opengles/
()
_jni (Android的 GLES的实现类 )
opengl/java/com/google/android/gles_jni/
JNI代码:
_jni JNI:
core/jni/
core/jni/
OpenGL的 JAVA和 JNI代码
的代码路径 :
opengl/java/android/opengl/
核心的文件 :
opengl/java/android/opengl/
public class GLSurfaceView extends SurfaceView
implements
Android的 是一个
OpenGL相关的包,它主要提供了为 OpenGL的
输出界面。
OpenGL的 JAVA和 JNI代码
在 Android的 JAVA应用中使用 OpenGL通常需要
类和
包的结合使用。
public class XXXActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
(savedInstanceState);
mGLSurfaceView = new XXXGlSurfaceView(this);
setContentView(mGLSurfaceView);
();
(true);
}
private GLSurfaceView mGLSurfaceView;
}
class XXXGlSurfaceView extends GLSurfaceView {
private class CubeRenderer implements {/* ... ...*/}
/* ... ...*/
}
谢谢!
Slide 1
Slide 2
Slide 3
Slide 4
Slide 5
Slide 6
Slide 7
Slide 8
Slide 9
Slide 10
Slide 11
Slide 12
Slide 13
Slide 14
Slide 15
Slide 16
Slide 17
Slide 18
Slide 19
Slide 20
Slide 21
Slide 22
Slide 23
Slide 24
Slide 25
Slide 26
Slide 27
Slide 28
Slide 29
Slide 30
Slide 31
Slide 32
Slide 33
Slide 34
Slide 35
Slide 36
Slide 37
Slide 38
Slide 39
Slide 40
Slide 41
Slide 42
Slide 43
Slide 44
Slide 45
Slide 46
Slide 47
Slide 48
Slide 49
Slide 50
Slide 51
Slide 52
Slide 53
Slide 54
Slide 55
Slide 56
Slide 57
Slide 58