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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM