Android apk 里面的画图分为2D和3D两种:2D是由Skia
来实现的,也就是我们在框架图上看到的SGL,SGL也会调用部分opengl
的内容来实现简单的3D效果;3D部分是由OpenGL|ES实现的,OpenGL|ES是Opengl的嵌入式版本,我们先了解一下Android
apk的几种画图方式,然后再来来看一看这一整套的图形体系是怎么建立的。
首先画图都是针对提供给应用 程序的一块内存填充数据
,没去研究过一个Activity是否就对应着底层的一个surface,但是应该都是对这块surface内存进行操作。因此说穿了就是我们要么调用
2D
的API画图,要么调用3D的API画图,然后将画下来的图保存在这个内存中,最后这个内存里面的内容会被Opengl渲染以后变为可以在屏幕上的像素信
息。
二、了解了2D,我们再来看看3D的画图方式。3D画图SDK上讲得很简单,只是提了一个通用的方式,就是继承一个View,然后在这个View里面获得
Opengl的句柄进行画图,道理应该来说是和2D一样的,差别就是一个是使用2D的API画图,一个是使用3D的。不过因为3D
openGl|ES具有一套本身的运行机制,比如渲染的过程控制等,因此Android为我们提供了一个专门的用在3D画图上的
GLSurfaceView。这个类被放在一个单独的包android.opengl里面,其中实现了其他View所不具备的操作:
(1) 具有OpenGL|ES调用过程中的错误跟踪,检查工具,这样就方便了Opengl编程过程的debug ;
(2)
所有的画图是在一个专门的Surface上进行,这个Surface可以最后被组合到android的View体系中 ;
(3)
它可以根据EGL的配置来选择自己的buffer类型,比如RGB565,depth=16
(这里有点疑问,SurfaceHolder的类型是SURFACE_TYPE_GPU,内存就是从EGL分配过来的?)
(4)
所有画图的操作都通过render来提供,而且render对Opengl的调用是在一个单独的线程中
(5)
Opengl的运行周期与Activity的生命周期可以协调
下面我们再看看利用GLSurface画3D图形的一个典型的Sequence
(1)
选择你的EGL配置(就是你画图需要的buffer类型) [optional] :
setEGLConfigChooser(boolean)
setEGLConfigChooser(EGLConfigChooser)
setEGLConfigChooser(int, int, int, int, int, int)
(2)
选择是否需要Debug信息 [optional] :
setDebugFlags(int)
setGLWrapper(GLSurfaceView.GLWrapper).
(3)
为GLSurfaceView注册一个画图的renderer : setRenderer(GLSurfaceView.Renderer)
(4) 设置reander mode,可以为持续渲染或者根据命令 来渲染,默认是continuous rendering
[optional]: setRenderMode(int)
这里有一个要注意的地方就是必须将Opengl的运行和Activity的生命周期绑定在一起,也就是说Activity
pause的时候,opengl的渲染也必须pause。另外GLSurfaceView还提供了一个非常实用的线程间交互的函数
queueEvent(Runnable),可以用在主线程和render线程之间的交互,下面就是SDK提供的范例:
class
MyGLSurfaceView extends GLSurfaceView {
private MyRenderer
mMyRenderer;
public void start() {
mMyRenderer =
...;
setRenderer(mMyRenderer);
}
public
boolean onKeyDown(int keyCode, KeyEvent event) {
if (keyCode
== KeyEvent.KEYCODE_DPAD_CENTER) {
queueEvent(new
Runnable() {
// This method will be called on the
rendering
// thread:
public void
run() {
mMyRenderer.handleDpadCenter();
}});
return true;
}
return
super.onKeyDown(keyCode, event);
}
}
GLSurfaceView是Android提供的一个非常值得学习 的类,它实际上是一个如何在View中添加画图线程的例子,如何在Java
中使用线程的例子,如何添加事件队列的例子,一个使用SurfaceView画图的经典Sequence,一个如何定义Debug信息的例子,觉得把它看
懂了可以学到很多知识
,具体的源码在:/framworks/base/opengl/java/android/opengl/GLSurfaceView.java 。
3D的内容基本到这里基本讲完了,剩下的主要是如何使用Opengl API的问题了,可以看看API
demo中简单的立方体,复杂的可以看看它那个魔方的实现。下面我们总结一下3D画图需要用到的包:
Android.opengl //主要定义了GLSurfaceView
javax.microedition.khronos.egl //java层的egl接口
javax.microedition.khronos.opengles //opengl API
三、了解了2D和3D基本的画图方法,我们再回过头来看看整个Android对Opengl和Skia的调用层次关系
3.1、首先来看2D,2D是主要使用的图形引擎,毕竟3D受制于其过高的硬件要求在手机上使用还是比较少,而且Skia也能部分实现类似于3D的效果,
因此可以说SKia实现了Android平台上绝大多数的图形工作。下面我们来看看从应用层到底层对skia的调用关系:
Android对skia的调用是一个比较经典
的调用过程,应用程序的几个包是在SDK中提供的;JNI放在框架的JNI目录下面的Graphic目录;skia是作为一个第三方组件放在
external目录下面。我们可以稍微了解一下skia的结构:
这里主要涉及到的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/src中有两个目录animator和view没有写入makefile的编译路径中,我觉得这两个目录是很重要的,不知道是现在
Android还没使用到,还是用其他的方式加载进去的。
要想在底层使用skia的接口来画图需要全面了解skia的一整套机制,实际上skia开源到现在还没多久,在网上能找到的资料是也是很粗浅的,如果将来
真需要在这方面下功夫肯定是需要一定的工作量的。
3.2、Android对3D的调用曾经让我迷惑了一段时间,因为在framewoks/base/core/jni这个目录一直没找到跟opengl相
关的内容,后面去仔细看看opengl里面的内容才知道Android把opengl的本地实现,JNI,java接口都放在/frameworks
/base/opengl下面了,而且它内部还带了一个工具可以生成JNI代码。
我们来看看opengl的目录结构:
/include 包含egl和gles所有的头文件
/java/android/opengl 这个目录包含的就是我们3D画图要使用到的GLSurfaceView
/java/com/google/android/gles_jni 这个目录包含一些自动生成的文件
/java/javax/microedition/khronos/egl 这就是应用层使用到的egl接口
/java/javax/microedition/khronos/opengl 这就是应用层使用到的opengl接口
/libagl 这个就是opengl主要的实现了
/libs 这里面包含两个库的实现,一个是libegl.so
还有一个是libGL|ES_CM.so
/tools 在我的理解这就是一个jni的生成工具
Opengl编程谁都知道是一个大工程,我觉得现在对3D的需求应该是很低的,很多效果我们使用skia也可以实现。所以我觉得将来的重点应该还是放在
skia上面。