Android的图形显示原理(GDI)一

Android GDI基本框架

在Android中所涉及的概念和代码最多,最繁杂的就是GDI相关的代码了。但是本质从抽象上来讲,这么多的代码和框架就干了一件事情:对显示缓冲区的操作和管理。

GDI主要管理图形图像的输出,从整体方向上来看,GDI可以被认为是一个物理屏幕使用的管理器。因为在实际的产品中,我们需要在物理屏幕上输出不同的窗口,而每个窗口认为自己独占屏幕的使用,对所有窗口输出,应用程序不会关心物理屏幕是否被别的窗口占用,而只是关心自己在本窗口的输出,至于输出是否能在屏幕上看见,则需要GDI来管理。

image

从最上层到最底层的数据流的分析可以看到实际上GDI在上层为GUI提供一个抽象的概念,就好像操作系统中的文件系统所提供文件,目录等抽象概念一样,GDI输出抽象成了文本,画笔,位图操作等设备无关的操作,让应用程序员只需要面对逻辑的设备上下文进行输出操作,而不要涉及到具体输出设备,以及输出边界的管理。GDI负责将文本、线条、位图等概念对象映射到具体的物理设备,所以GDI的在大体方向上可以分为以下几大要素:

画布

字体

文本输出

绘画对象

位图输出

Android的GDI系统

Android的GDI系统所涉及到概念太多,加之使用了OpenGL使得Android的层次和代码很繁杂。但是我们对于Android的GDI系统需要了解的方面不是他的静态的代码关系,而是动态的对象关系,在逻辑运行的架构上理解GDI。我们首先还是需要从代码结构开始我们的理解。

Frameworks/Libs/Surfaceflinger

Frameworks/base/core/jni/android_view_Surface.cpp

Frameworks/base/core/java/android/view/surface.java

Frameworks/base/Graphics:绘图接口

Frameworks/Libs/Ui

External/Skia

其中External/Skia是一个C++的2D图形引擎库,Android的2D绘制系统都是建立在该基础之上.Skia完成了:文本输出,位图,点,线,图像解码等功能。

我在这里给出Android GDI的基本框架示意图。

image

对于上面的GDI架构图我们只是一个大概的了解,我们有太多的问题需要解决,有太多的疑问需要得到答案,我就一直在想,为什么设计者有提出如此众多的概念,这个概念的背景是什么?他要管理什么,他要抽象什么?从前面知道,Android的整个设计理念就是无边界化,他是如何穿透Linux进程这个鸿沟来达到无边界的?Surface,Canvas, Layer,LayerBase, NativeBuffer,SurfaceFlinger,SurfaceFlingerClient这些到底是一个什么东西?如何管理,传递的是什么?创建的是什么?这些都是抽象的概念,绘画的终极的缓冲区到底是如何管理的?缓冲区到底在哪里?

我们还是看看做终极的,最本质的设计概念,在从这些概念出发,来探讨这些概念的形成过程,是否有必要去生成写概念。SurfaceFlinger本质上干什么的?SurfaceFlinger的确就是这个意义:应用程序通过SurfaceFlinger将自己的“Surface”投掷到屏幕缓冲区。至于如何投掷的,我们将会在后面详细描述。

 

Android GDI之屏幕设备管理-动态链接库
 
       万丈高楼从地起,从最根源的硬件帧缓冲区开始。我们知道显示FrameBuffer在系统中就是一段内存,GDI的工作就是把需要输出的内容放入到该段内存的某个位置。我们从基本的点(像素点)和基本的缓冲区操作开始。

1 基本知识
 
1.1点的格式
 
     对于不同的LCD来讲,FrameBuffer的二进制格式不一样,并且可以分为两部分:

   1)点的格式:通常将Depth,即表示多少位表示一个点。

1位表示一个点 
2位表示一个点  
16位表示一个点  
32位表示一个点(Alpha通道)  
   2) 点内格式:RGB分量分布表示。 

例如对于我们常见的16位表示一个点
 image


1.2.格式之间的转换

所以屏幕输出实际上是一个值映射的关系。我们可以有如下的点格式转换,
 image


源格式可能来自单色位图和彩色位图,对于具体的目标机来讲,我们的目标格式可能就是一种,例如16位(5/6/5)格式。其实就只存在一种格式的转换,即从目标格式都是16位格式。
 

image
但是,在设计GDI时,基本要求有一个可移植性好,所以我们还是必须考虑对于不同点格式LCD之间的转换操作。所以在GDI的驱动程序中涉及到如下几类主要操作:
 
区域操作(Blit):我们在显示缓冲区上做的最多的操作就是区块搬运。由此,很多的应用处理器使用了硬件图形加速器来完成区域搬运:blit.从我们的主要操作的对象来看,可以分为两个方向:
 

1)内存区域到屏幕区域 

2)屏幕区域到屏幕区域
 
3)屏幕区域到内存区域
 
4)内存区域到内存区域
 
在这里我们需要特别提出的是,由于在Linux不同进程之间的内存不能自由的访问,使得我们的每个Android应用对于内存区域和屏幕缓冲区的使用变得很复杂。在Android的设计中,在屏幕缓冲区和显示内存缓冲区的管理分类很多的层次,最上层的对象是可以在进程间自由传递,但是对于缓冲区内容则使用共享内存的机制。
 
基于以上的基础知识,我们可以知道:
 

(1)代码中Config及其Format的意义所在了。也就理解了兼容性的意义:采用同硬件相同的点的描述对象
 
(2)所有屏幕上图形的移动都是显示缓冲区搬运的结果。
 
1.2图形加速器
 
    应用处理器都可能带有图形加速器,对于不同的应用处理器对其图形加速器可能有不同的处理方式,对于2D加速来讲,都可归结为Blit。多为数据的搬运,放大缩小,旋转等。
 
2 Android的缓冲区抽象定义 
  
    不同的硬件有不同的硬件图形加速设备和缓冲内存实现方法。Android Gralloc动态库抽象的任务就是消除不同的设备之间的差别,在上层看来都是同样的方法和对象。在Moudle层隐藏缓冲区操作细节。Android使用了动态链接库gralloc.xxx.so,来完成底层细节的封装。
 
2.1 本地定义@hardware\libhandware\modules\gralloc
 
每个动态链接库都是用相同名称的调用接口:
 

1)硬件图形加速器的抽象:BlitEngine,CopyBit的加速操作。
 
2)硬件FrameBuffer内存管理
 
3)共享缓存管理

从数据关系上我们来考察..动态链接库的抽象行为:在层次:Hardware.c@hardware\libhardware 中对动态链接库中的内容作了全新的包装。/system/lib/hw/gralloc.xxx.so动态库文件。从文件Gralloc.h(handware\libhardware\include\hardware)是抽象的结果:hw_get_module从gralloc.xxx.so提取了HAL_MODULE_INFO_SYM(SYM变量)
 image


从展露在外部的数据结构,我们在@Gralloc.cpp看到到了这样的布局:
 

static struct hw_module_methods_t gralloc_module_methods = {
 
open: gralloc_device_open
 
};
 
struct private_module_t HAL_MODULE_INFO_SYM = {
 
    base: {
 
        common: {
 
            tag: HARDWARE_MODULE_TAG,
 
             …
 
            id: GRALLOC_HARDWARE_MODULE_ID,
 
            name: "Graphics Memory Allocator Module",
 
            author: "The Android Open Source Project",
 
            methods: &gralloc_module_methods
 
        },
 
registerBuffer: gralloc_register_buffer,
 
unregisterBuffer: gralloc_unregister_buffer,
 
lock:  gralloc_lock,
 
unlock:  gralloc_unlock,
 
    },
 
    framebuffer: 0,
 
    flags: 0,
 
    numBuffers: 0,
 
    bufferMask: 0,
 

 
};
 
我们建立了什么对象来支撑缓冲区的操作?
 

buffer_handle_t:外部接口。
 
methods.open,registerBuffer,unregisterBuffer,lock,unlock
 
下面是外部接口和内部对象的结构关系,该类型的结构充分利用C Struct的数据排列特性:基本结构体放置在最前面,本地私有放置在后面,满足了抽象的需要。
 
typedef const native_handle* buffer_handle_t;
 
private_module_t  HAL_MODULE_INFO_SYM 向往暴露的动态链接库接口,通过该接口,我们直接可以使用该对象。
 image



几个接口函数的解释:
 
(1)fb_post
 
对于帧缓冲区实际地址并不需要向上层报告,所有的操作都是通过fb_post了完成。
 
fp_post的任务就是将一个Buffer的内容传递到硬件缓冲区。其实现方式有两种:
 image


(方式1)无需拷贝动作,是把Framebuffer的后buffer切为前buffer,然后通过IOCTRL机制告诉FB驱动切换DMA源地地址。这个实现方式的前提是Linux内核必须分配至少两个缓冲区大小的物理内存和实现切换的ioctrol,这个实现快速切换。
 

 

(方式2)利用Copy的方式。不修改内核,则在适配层利用从拷贝的方式进行,但是这个是费时了。
 

 

(2)gralloc的主要功能是要完成:
 
     1)打开屏幕设备 "/dev/fb0",,并映射硬件显示缓冲区。
 
     2)提供分配共享显示缓存的接口
 
     3)提供BiltEngine接口(完成硬件加速器的包装)
 
(3)gralloc_alloc输出buffer_handle_t句柄。
 
     这个句柄是共享的基本依据,其基本原理在后面的章节有详细描述。
 
3 总结
 
       总结一下,/system/lib/hw/gralloc.xxx.so是跟硬件体系相关的一个动态链接库,也可以叫做Android的硬件抽象层。他实现了Android的硬件抽象接口标准,提供显示内存的分配机制和CopyBit等的加速实现。而如何具体实现这些功能,则跟硬件平台的配备有关系,所以我们看到了对于与不同的硬件架构,有不同的配置关系。

本篇文章来源于 Linux公社网站(www.linuxidc.com)  原文链接:http://www.linuxidc.com/Linux/2011-04/33966p24.htm

转载于:https://www.cnblogs.com/alfredzzj/archive/2012/02/26/2368891.html

### 回答1: JS报uncaught syntaxerror: unexpected token <错误通常是因为在HTML页面中引入JS脚本的链接出现了问题。这个错误提示显示出了JS脚本引入的标签遭遇了一个不期望的"<"符号。 解决这个问题的方法是检查HTML页面中引用的JS脚本链接是否正确且完整。可以使用浏览器开发者工具找到报错的具体行号,并检查该行的链接是否正确。如果链接正确,可以尝试将JS代码复制到一个单独的JS文件中,然后再通过链接方式引用。如果仍然存在错误,可能是JS代码本身的问题,需要检查JS代码语法和逻辑是否正确。如果仍然无法解决问题,建议寻求更专业的技术支持或寻找更多相关教程和资料进行学习。 ### 回答2: 在使用JavaScript开发网站时,我们常常会遇到“Uncaught SyntaxError: Unexpected token <”这个错误。这个错误通常出现在我们的JavaScript代码中出现了“<”符号。这个符号在HTML代码中被广泛使用来表示标签的开头,但在JavaScript代码中,这个符号是一个特殊的字符,它表示字符串或表达式的小于(比较)运算符。因此,当我们在JavaScript代码中使用了“<”符号时,浏览器会认为这是一个意外的符号,从而引发了这个错误。 解决这个错误的方法有以下几种: 1. 检查JavaScript代码是否正确:首先,我们应该检查我们的JavaScript代码是否有任何语法错误或拼写错误。这通常可以通过使用浏览器的控制台来完成,控制台会显示JavaScript代码中的错误信息和行号。 2. 使用转义字符:如果我们必须在JavaScript代码中使用“<”符号,我们可以使用转义字符“\”来转义它。例如,我们可以使用“<”符号代替“\<”符号。 3. 将JavaScript代码放在正确的位置:有时,当我们将JavaScript代码放在错误的位置时,例如在HTML标签中间或在<script>标签之外,浏览器会将其解释为HTML代码,从而引发这个错误。因此,我们应该确保我们的JavaScript代码放在正确的位置,例如在<script>标签内或外部的JavaScript文件中。 4. 检查文件路径:在引用外部JavaScript文件时,我们应该确保文件路径是正确的。如果文件路径不正确,浏览器将无法加载JavaScript文件,从而导致这个错误。 5. 确保服务器已正确配置:如果我们的网站使用了服务器端脚本语言(例如PHP或ASP),我们应该确保服务器已正确配置,以便正确解释JavaScript代码。否则,浏览器将尝试在客户端解释服务器端的JavaScript代码,并引发这个错误。 总之,我们可以通过检查代码的语法及结构、使用转义字符、放置代码的位置和检查文件路径以及确保服务器已正确配置等方法来解决JavaScript中的“Uncaught SyntaxError: Unexpected token <”错误。 ### 回答3: JS在页面中报错时,返回的错误信息通常都很晦涩难懂,容易让人不知所措。uncaught syntaxerror: unexpected token <错误通常表示代码语法错误,通常是由于代码中缺少某个符号或引号、拼写错误等原因导致的。 首先,检查代码中是否有缺省的分号、括号、引号等未闭合。这些都是常见的语法错误,检查并及时修复可以大大缩短解决问题的时间。 其次,如果确定代码本身没有问题,则需要检查引入的外部文件,例如CSS、JS等文件。如果其中有文件错误,那么可能会导致JS也无法正常执行,所以要对所有文件进行检查。 另外,如果代码中使用了特殊字符、HTML标签等,也可能会引发这种错误。此时可以将这些内容用escape()函数进行转义处理,避免出现这种错误。 最后,可以使用开发者工具中的console.log()对代码进行逐行调试,定位出错误出现的位置。这种方法比较耗时,但可以有效地解决问题。 总之,解决uncaught syntaxerror: unexpected token <错误需要耐心,仔细地检查代码,尝试各种可能的解决方法。同时,建议平时写代码时注意细节,避免出现常见的语法错误。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值