microwindows位图解析
From: http://blog.csdn.net/bisword/article/details/2740054
第1章 microwinodows圖形顯示框架
1.1 microwindows體系結(jié)構(gòu)
Microwindows 采用了分層結(jié)構(gòu)設(shè)計方法,其層次結(jié)構(gòu)如下圖所示。同時, 這里也列出 Microwindows 源代碼目錄樹下的主要目錄結(jié)構(gòu),以便于對照參考。
microwindows --bin
--Configs
nano-X /win32 API --demos
--drivers
engine --engine
色彩控制|blitting|區(qū)域剪裁|clipping|圖形繪制 microwindows-0.90 --fonts
--includes --lib
顯示驅(qū)動 | 鍵盤驅(qū)動 | 鼠標(biāo)/觸摸屏驅(qū)動 --mwin
--nanox
圖1 microwindows體系結(jié)構(gòu)
Microwindows 提 供 兩 種 類 型 的 API , 一 種 叫 Microwindows API,是與win32/WinCE GDI 相兼容的 API 接口,另一種叫 Nano-X API,是與 Linux 上著名的 X Window 相兼容的 API。實現(xiàn) Microwindows API 的程序放置在 mwin 目錄下,實現(xiàn) Nano-X API 的程序放置在 nanox 目錄下。
API層之下是設(shè)備無關(guān)的圖形引擎層,該層負(fù)責(zé)底層繪圖原語,實現(xiàn)各種圖形繪制, 各種圖像格式處理,字體處理等功能。該層的程序主要放置在 engine 和 fonts 目錄下。
在最下層是設(shè)備驅(qū)動層,該層為各類設(shè)備提供驅(qū)動程序,目前包括顯示/觸摸屏,鼠標(biāo),鍵盤等設(shè)備的驅(qū)動程序,驅(qū)動層負(fù)責(zé)與物理設(shè)備進(jìn)行交互通信,該層的程序都放置在 drivers 目錄下。
1.2 引擎層
1.2.1 引擎層剖析
圖形引擎層在 Microwindows 中起著承上啟下的作用,既對下層設(shè)備驅(qū)動的功能實行封裝,又為上層應(yīng)用編程接口 API 提供服務(wù),系統(tǒng)核心圖形繪制功能都是在該層實現(xiàn)的。由于我們這次我們主要講解的是關(guān)于圖像處理方面的知識,所以其他不相關(guān)的我們暫且不再涉及。以下我們從,圖形上下文,繪圖區(qū)域,裁剪,等方面介紹圖形引擎層的功能與實現(xiàn),并先列舉 engine 目錄下的關(guān)于圖像處理主要文件及其完成的功能以供參考:
Devdraw.c:主要的圖形繪制函數(shù),包括調(diào)色板操作,背景繪制,畫線,畫圓,多邊型與填充,位操作及圖形的放大和縮小等。
Devclip.c : 剪裁操作。
Devrgn.c :實現(xiàn)圖形的布爾操作。
Devpalx.c :處理 1,2,4,8 位調(diào)色板的映射。
Devpalgray4.c:處理 4 級灰度映射。
Devimage.c :處理對 GIP,BMP,JPEG,PPM,PNG,TIFF 等圖形格式的顯示操作。
Microwindows的中間層是圖形引擎層(也稱為設(shè)備與平臺無關(guān)層),圖形引擎層為應(yīng)用層提供了一系列的繪圖函數(shù),因為Microwindows系統(tǒng)中最核心的圖形函數(shù)是圖形引擎層通過調(diào)用下層的硬件設(shè)備驅(qū)動程序?qū)崿F(xiàn)的,所以該層與硬件無關(guān)。在microwindows的繪圖過程中用戶應(yīng)用程序通常不直接調(diào)用引擎層的繪圖函數(shù),而是通過調(diào)用最上層所提供的編程接口API,來實現(xiàn)圖像的繪制。這些繪制函數(shù)的命名遵循 GdXXX()的模式,第一個參數(shù)是 SCREENDEVICE 結(jié)構(gòu)的指針,在 device.h 中定義,描述了顯示器的底層特性,是顯示設(shè)備驅(qū)動程序的接口。SCREENDEVICE 結(jié)構(gòu)中包含了顯示設(shè)備的物理特性,比如橫向和縱向的大小,顯示模式,色彩,顯示內(nèi)存的大小等屬性,此外該結(jié)構(gòu)中也包含了各種和屏幕設(shè)備相關(guān)的操作的函數(shù)指針,所有的屏幕坐標(biāo)都是COORD類型的,以設(shè)備坐標(biāo)的方式指明,即對屏幕左上角的位移。顏色總是以RGB COLORVAL值的方式傳送給圖形引擎。他們可能在轉(zhuǎn)換為調(diào)色板的索引值后,作為PIXELVAL值被傳送給顯示硬件。在下面的底層驅(qū)動的章節(jié)中我會詳細(xì)介紹他們,下面我們對圖形引擎中的用到的概念進(jìn)行解釋。
1.2.1.1 圖形上下文
Nano-X 中使用圖形上下文描述繪圖屬性。要在屏幕上繪圖,需要先調(diào)用GrNewGC()函數(shù)分配一個圖形上下文數(shù)據(jù)結(jié)構(gòu),然后返回數(shù)據(jù)結(jié)構(gòu)的 ID 值。圖形上下文實際上標(biāo)識了一個 GR_GC_INFO 結(jié)構(gòu)體,如下所示,這個結(jié)構(gòu)體包含了前景,背景,字體,繪圖模式和繪圖區(qū)域等信息,設(shè)置圖形上下文時使用函數(shù)進(jìn)行操作,而不是對結(jié)構(gòu)體直接操作。
typedef struct {
GR_GC_ID gcid; /*圖形上下文ID */
int mode; /*繪畫模式*/
GR_REGION_ID region; /*繪畫區(qū)域*/
int xoff; /*x偏移量(以繪畫區(qū)域的左上角為參照)*/
int yoff; /*y偏移量*/
GR_FONT_ID font; /*字體大小設(shè)置*/
GR_COLOR foreground; /*前景色RGB值或像素值*/
GR_COLOR background; /* 背景色RGB值或像素值 */
GR_BOOL fgispixelval; /*如果前景色為像素值則定義為TRUE?*/
GR_BOOL bgispixelval; /* 如果背景色為像素值則定義為TRUE */
GR_BOOL usebackground; /*位圖作為背景 */
GR_BOOL exposure; /* 向 GrCopyArea 發(fā)出顯示事件*/
} GR_GC_INFO;
1.2.1.2 繪圖區(qū)域
矩形區(qū)域被用來描述屏幕上點的集合。簡單的情況下,矩形區(qū)域可以使用一個長方形來描述,但是在復(fù)雜情況下可能要使用復(fù)雜的數(shù)據(jù)結(jié)構(gòu)描述。Microwindows 中繪圖區(qū)域使用互不重疊的矩形區(qū)域表示,因此對于一個不規(guī)則的凸邊形要使用多個不重疊的矩形拼和。
1.2.1.3裁剪
Microwindows 中裁剪和以上提及的繪圖區(qū)域有著密切的關(guān)系。任何時候, 圖形引擎都有一個繪圖區(qū)域,這個區(qū)域是由一系列的矩形組成的,所有對圖形的操作都是在這個區(qū)域進(jìn)行。系統(tǒng)中實現(xiàn)裁剪有兩種方法,都使用了GdSetClipRects()函數(shù)將裁剪區(qū)域傳入圖形引擎, 此后所有的繪圖函數(shù)都要先判斷繪圖區(qū)域是否能被繪制,然后才進(jìn)行繪制。
1.3底層驅(qū)動
人機交互系統(tǒng)必然都包含輸入/輸出設(shè)備,Microwindows 中控制的設(shè)備包括顯示器/屏輸出設(shè)備和鼠標(biāo)/鍵盤/觸摸屏等輸入設(shè)備。設(shè)備驅(qū)動程序的接口函數(shù)在 device.h 中定義,至少包含一個屏幕驅(qū)動程序,一個鼠標(biāo)驅(qū)動程序和一個鍵盤驅(qū)動程序。圖形引擎層能夠直接調(diào)用這些驅(qū)動程序進(jìn)行相應(yīng)的硬件設(shè)備操作, 從而實現(xiàn)設(shè)備無關(guān)性, 使用這樣的層次結(jié)構(gòu)能保證添加其他的硬件設(shè)備時不影響整個系統(tǒng)的正常工作。
1.3.1屏幕驅(qū)動
microwindows屏幕驅(qū)動是基于Linux內(nèi)核中framebuffer,這要求在編譯內(nèi)核的時候選擇支持framebuffer編譯參數(shù)選項。它是通過fd=open(env="/dev/fb0")打開,用SCREENDEVICE的指針PSD指向這片顯存,然后對這片顯存根據(jù)屏幕的不同位色設(shè)置情況為中間引擎層提供相應(yīng)的圖形操作支持,包括畫點線、圖片顯示、屏幕拷貝以及中西文字的顯示等等。由于microwindows自身的特性他被被設(shè)計成能夠極其容易的向Microwindows中移植新硬件,因此必須提供一些訪問硬件的入口點,同時提供其他的函數(shù)保證只使用小部分的核心函數(shù)集就可以完成此任務(wù)。例如:屏幕驅(qū)動程序必須實現(xiàn)ReadPixel,DrawPixel,DrawHorzline和DrawVertLine。這些函數(shù)在顯存中讀取或?qū)懭胍粋€像素,或畫一條水平或垂直的線。位圖傳送允許Microwindows執(zhí)行屏幕外的作圖。Microwindows允許任何能在屏幕上進(jìn)行的圖形操作在屏幕外進(jìn)行,然后再將圖形傳送到物理顯存。下面我們就介紹顯示設(shè)備驅(qū)動使用的典型數(shù)據(jù)結(jié)構(gòu),參考該結(jié)構(gòu)可以幫助了解顯示設(shè)備的基本功能。
顯示驅(qū)動接口數(shù)據(jù)結(jié)構(gòu):
typedef struct _mwscreendevice {
MWCOORD xres; /* X screen res (real) */
MWCOORD yres; /* Y screen res (real) */
MWCOORD xvirtres; /* X drawing res (will be flipped in portrait mode) */
MWCOORD yvirtres; /* Y drawing res (will be flipped in portrait mode) */
int planes; /* # planes*/
int bpp; /* # bpp*/
int linelen; /* line length in bytes for bpp 1,2,4,8*/
/* line length in pixels for bpp 16, 24, 32*/
int size; /* size of memory allocated*/
long ncolors; /* # screen colors*/
int pixtype; /* format of pixel value*/
int flags; /* device flags*/
void * addr; /* address of memory allocated (memdc or fb)*/
PSD (*Open)(PSD psd);
void (*Close)(PSD psd);
void (*GetScreenInfo)(PSD psd,PMWSCREENINFO psi);
void (*SetPalette)(PSD psd,int first,int count,MWPALENTRY *pal);
void (*DrawPixel)(PSD psd,MWCOORD x,MWCOORD y,MWPIXELVAL c);
MWPIXELVAL (*ReadPixel)(PSD psd,MWCOORD x,MWCOORD y);
void (*DrawHorzLine)(PSD psd,MWCOORD x1,MWCOORD x2,MWCOORD y,MWPIXELVAL c);
void (*DrawVertLine)(PSD psd,MWCOORD x,MWCOORD y1,MWCOORD y2,
MWPIXELVAL c);
void (*FillRect)(PSD psd,MWCOORD x1,MWCOORD y1,MWCOORD x2,MWCOORD y2,MWPIXELVAL c);
PMWCOREFONT builtin_fonts;
/* *void (*DrawText)(PSD psd,MWCOORD x,MWCOORD y,const MWUCHAR *str,
int count, MWPIXELVAL fg, PMWFONT pfont);***/
void (*Blit)(PSD destpsd,MWCOORD destx,MWCOORD desty,MWCOORD w,
MWCOORD h,PSD srcpsd,MWCOORD srcx,MWCOORD srcy,long op);
void (*PreSelect)(PSD psd);
void (*DrawArea)(PSD psd, driver_gc_t *gc, int op);
int (*SetIOPermissions)(PSD psd);
PSD (*AllocateMemGC)(PSD psd);
MWBOOL (*MapMemGC)(PSD mempsd,MWCOORD w,MWCOORD h,int planes,int bpp,int linelen,int size,void *addr);
void (*FreeMemGC)(PSD mempsd);
void (*StretchBlit)(PSD destpsd,MWCOORD destx,MWCOORD desty,
MWCOORD destw,MWCOORD desth,PSD srcpsd,MWCOORD srcx,
MWCOORD srcy,MWCOORD srcw,MWCOORD srch,long op);
void (*SetPortrait)(PSD psd,int portraitmode);
int portrait; /* screen portrait mode*/
PSUBDRIVER orgsubdriver; /* original subdriver for portrait modes*/
void (*StretchBlitEx) (PSD dstpsd, PSD srcpsd,
MWCOORD dest_x_start, MWCOORD dest_y_start,
MWCOORD width, MWCOORD height,
int x_denominator, int y_denominator,
int src_x_fraction, int src_y_fraction,
int x_step_fraction, int y_step_fraction,
long op);
} SCREENDEVICE;
xres :表示屏幕的寬 (以像素為單位);
yres :表示屏幕的高 (以像素為單位);
planes :當(dāng)處于平面顯示模式時,planes 用于記錄所使用的平面數(shù),如平面模式相對的時
線性模式,此時該變量沒有意義。通常將其置為0。
bpp :表示每個像素所使用的比特數(shù),可以為1,2,4,8,15,16,24,32。
linelen :對與1,2,4,8比特每像素模式,它表示一行像素使用的字節(jié)數(shù),對于大于8比
特每像素模式,它表示一行的像素總數(shù)。
size :表示該顯示模式下該設(shè)備使用的內(nèi)存數(shù)。linelen 和 size 的存在主要是為了方便為內(nèi)
存屏幕設(shè)備分配內(nèi)存。
gr_foreground 和 gr_background :表示該內(nèi)存屏幕的前景顏色和背景顏色,主要被一些
GDI 函數(shù)使用。
gr_mode :說明如何將像素畫到屏幕上,可選值為:MODE_SET MODE_XOR MODE_OR
MODE_AND MODE_MAX,比較常用的是MODE_SET和MODE_XOR
flags :該屏幕設(shè)備的一些選項,比較重要的是 PSF_MEMORY 標(biāo)志,表示該屏幕設(shè)備代
表物理屏幕設(shè)備還是一個內(nèi)存屏幕設(shè)備。
addr :每個屏幕設(shè)備都有一塊內(nèi)存空間用來作為存儲像素。addr 變量記錄了這個空間的起
始地址。
下面的函數(shù)是屏幕驅(qū)動的接口函數(shù),我將會在下面驅(qū)動接口中解析他們,在這里就不再介紹。
1.3.2驅(qū)動接口
在上面的文章中我們看到這樣一句話:顯示驅(qū)動是整個系統(tǒng)中最為復(fù)雜的部分,但是驅(qū)動程序的復(fù)雜性卻換來了移植的方便性,microwindows是怎么讓復(fù)雜的顯示驅(qū)動來方便移動呢?就是因為系統(tǒng)的顯示驅(qū)動程序里只有幾個接口和硬件設(shè)備直接交互,其他程序提供核心的繪圖操作比如讀取像素,繪制像素,繪制更復(fù)雜的圖形等,這些繪圖操作對系統(tǒng)映射的顯存進(jìn)行操作,讀寫像素點的信息。舉個通俗的例子,現(xiàn)在的轎車和以前的轎車在和剛開始發(fā)明的時候的駕駛方式幾乎沒有什么改變,都是離合,油門,方向盤,這個固定模式就好比我們的接口,他就是這樣,不管你的車性能多好,多高級,都是內(nèi)部的事,駕駛方式都是一樣的。來讓我們來看看microwindows的離合,油門,方向盤。在microwindows里們不論是畫多少位圖像的驅(qū)動我們都有統(tǒng)一的接口,都有統(tǒng)一的功能函數(shù)。下們就是microwindows驅(qū)動的接口,如果有一天我們中的那位發(fā)明一個microwidnows沒有的圖像格式,如果還要用microwindows的話,那么我們就要自己來寫驅(qū)動了,不過不要頭大,我們的驅(qū)動中只要包括下面接口中的函數(shù)就可以了,這就是microwindows的馬克思理論性,永遠(yuǎn)沒有盡頭,永遠(yuǎn)可以延伸和擴展。
typedef struct {
int (*Init)(PSD psd);
void (*DrawPixel)(PSD psd, int x, int y, gfx_pixel c);
gfx_pixel (*ReadPixel)(PSD psd, int x, int y);
void (*DrawHLine)(PSD psd, int x, int y, int w, gfx_pixel c);
void (*PutHLine) (GAL gal, int x, int y, int w, void* buf);
void (*GetHLine) (GAL gal, int x, int y, int w, void* buf);
void (*DrawVLine)(PSD psd, int x, int y, int w, gfx_pixel c);
void (*PutVLine) (GAL gal, int x, int y, int w, void* buf);
void (*GetVLine) (GAL gal, int x, int y, int w, void* buf);
void (*Blit)(PSD dstpsd, int dstx, int dsty, int w, int h, PSD srcpsd, int srcx, int srcy);
void (*PutBox)( GAL gal, int x, int y, int w, int h, void* buf );
void (*GetBox)( GAL gal, int x, int y, int w, int h, void* buf );
void (*PutBoxMask)( GAL gal, int x, int y, int w, int h, void *buf);
void (*CopyBox)(PSD psd,int x1, int y1, int w, int h, int x2, int y2);
} SUBDRIVER, *PSUBDRIVER;
下面介紹各接口函數(shù):
DrawPixel(PSD psd, int x, int y, gfx_pixel c):
功能:在給定的地址上畫一個像素點。
參數(shù)介紹:psd在這里我們對psd的應(yīng)用就是psd的基地址,x,y就是該點相對于基址的偏移量他的算法是這樣的。addr = x * pitch + y ,addr就是該點的所要畫的位置。gfx_pixelc就是傳遞過來的像素值,該函數(shù)解析后畫到addr這個位置。絢爛多彩的圖像就是靠勤勞的microwindows這樣一點一點畫出來了。
ReadPixel(PSD psd, int x, int y):
功能:在給定的地址上讀出一個像素值。
參數(shù)介紹:psd在該函數(shù)中的用處還是他所指向的顯存的基地址,x,y就是該點相對于基址的偏移量,他的算法和上面的方式是相同的,然后就是該函數(shù)把該點像素解析后返回該像素的RGB值。
DrawHorzLine(PSD psd,MWCOORD x1,MWCOORD x2,MWCOORD y,
MWPIXELVAL c):
功能:在兩點之間畫橫線。
參數(shù)介紹:psd的作用還是向程序提供基地址,雖然該函數(shù)有三個坐標(biāo)數(shù)據(jù),其實他代表的是兩個坐標(biāo)點,(x1,y),(x2,y).然后在y的位置從x1向x2從左到右畫線。c就是畫線時的填充像素了。
DrawVertLine(PSD psd,MWCOORD x,MWCOORD y1,MWCOORD y2,
MWPIXELVAL c):
功能:在兩點之間畫豎線。
參數(shù)介紹:psd作用還是提供繪圖的基地址。x,y1,y2的意思是在(x,y1)(x,y2)兩點見畫豎線。
c的作用還是畫線時的像素填充值。提醒:不論是畫豎線還是橫線,我們的microwindows都沒有偷懶,把最后一點也畫了,也就是我們的線是有頭有尾是完整的。
FillRect(PSD psd,MWCOORD x1,MWCOORD y1,MWCOORD x2,MWCOORD y2,
MWPIXELVAL c):
功能:填充一個矩形,也就是畫一個實心的矩形。
參數(shù)介紹:聰明的我們也許看了上兩個這個就不用解釋了把,是的和你認(rèn)為的一樣,就是在x1,y1,x2,y2的坐標(biāo)范圍內(nèi)用c的像素值來填充。
PutHLine,GetHLine,PutVLine,GetVLine,PutBox,GetBox,PutBoxMask
Get* 函數(shù)用于從屏幕拷貝像素到一塊內(nèi)存區(qū),而Put*函數(shù)用于將存放于內(nèi)存區(qū)的像素畫到屏幕上。PutBoxMask 與PutBox的唯一區(qū)別是要畫的像素如果是白色,就不會被畫到屏幕上,從而達(dá)到一種透明的效果。
Blit,CopyBox
Blit 用于在不同的屏幕設(shè)備(物理的或者內(nèi)存的)之間拷貝一塊像素點,CopyBox則用于在同一屏幕上實現(xiàn)區(qū)域像素的拷貝。如果使用的是線性模式,Blit的實現(xiàn)非常簡單,直接memcpy 就可以了,而CopyBox 為了防止覆蓋問題,必須根據(jù)不同的情況,采用不同的拷貝方式,比如從底到頂?shù)卓截?#xff0c;當(dāng)新老位置在同一水平位置并且重復(fù)時,則需要利用緩沖間接拷貝。如果使用平面顯示模式,這里就比較復(fù)雜了。因為內(nèi)存設(shè)備總是采用線性模式的,所以就要判斷是物理設(shè)備還是內(nèi)存設(shè)備,再分別處理。這也大大地增加了fbvga16 實現(xiàn)的代碼。
1.3.3驅(qū)動選擇
我們知道,圖形顯示有個顯示模式的概念,一個像素可以用一位比特表示,也可以用2,4,8,15,16,24,32個比特表示,另外,VGA16標(biāo)準(zhǔn)模式使用平面圖形模式,而VESA2.0使用的是線性圖形模式。所以即使是同樣基于Framebuffer 的驅(qū)動,不同的模式也要使用不同的驅(qū)動函數(shù):那么microwidnows是怎樣選擇驅(qū)動的呢。寫到這里我不禁要感慨一下,今天看到這篇文章的人是多么的幸福,有人已經(jīng)為你總結(jié)好了,可我可是一天幾行,請教無數(shù)善良的人們才知道的。
我說的這個內(nèi)容是在microwindows/src/nanox/srvmain.c中,當(dāng)我們啟動服務(wù)端后microwindows的初始化也就隨之開始了,通過GrOpenScreen我們來到了/microwindows/src/drivers/scr_fb.c中的fb_open內(nèi)部,我們就是通過select_fb_subdriver來進(jìn)行選擇,剛才在fb_open屏幕驅(qū)動已經(jīng)對psd進(jìn)行了初始化,他的過程是這樣的首先我們把framebuffer映射到我們申請的地址,然后通過FrameBuffer 中的ioctl函數(shù)
(下面有詳細(xì)解釋),framebuffer設(shè)備提供了若干 ioctl 命令,通過這些命令,可以獲得顯示設(shè)備的一些固定信息(比如顯示內(nèi)存大小)、與顯示模式相關(guān)的可變信息(比如分辨率、象素結(jié)構(gòu)、每掃描線的字節(jié)寬度),以及偽彩色模式下的調(diào)色板信息等等。然后microwindox根據(jù)ioctl函數(shù)返回的屏幕對psd進(jìn)行初始化,然后根據(jù)psd->bpp的不同讓driver指針指向不同的驅(qū)動地址,來選擇對應(yīng)的驅(qū)動,也就說驅(qū)動的選擇是microwindows根據(jù)framebuffer的返回值來選擇對應(yīng)的驅(qū)動。就這樣microwindows草船借了箭.
第2章 microwindows基于framebuffer顯像原理
2.1 framebuffer簡介
Framebuffer機制模仿顯卡的功能,將顯卡硬件結(jié)構(gòu)抽象掉,可以通過 Framebuffer的讀寫直接對顯存進(jìn)行操作。用戶可以將Framebuffer看成是顯示內(nèi)存的一個映像,將其映射到進(jìn)程地址空間之后,就可以直接進(jìn)行讀寫操作,而寫操作可以立即反應(yīng)在屏幕上。FrameBuffer 設(shè)備還提供了若干 ioctl 命令,通過這些命令(上面的驅(qū)動選擇中用到)可以獲得顯示設(shè)備的一些固定信息(比如顯示內(nèi)存大小)、與顯示模式相關(guān)的可變信息(比如分辨率、象素結(jié)構(gòu)、每掃描線的字節(jié)寬度),以及偽彩色模式下的調(diào)色板信息。
2.2 framebuffer在microwindows圖形解析中的應(yīng)用
如果在microwidnows使用framebuffer我們首先會在microwidnows的預(yù)編譯選項中,選中framebuffer,然后運行microwidnow服務(wù)端在初始化過程中打開幀緩沖設(shè)備,過程如下:(microwindows-0.90/src/nanox/srvmain.c中的GsInitialize/GdOpenScreen/scrdev/fbopen()函數(shù)),
if(!(env = getenv("FRAMEBUFFER")))
{
env = "/dev/fb0";
}
fb = open(env, O_RDWR);
從上面代碼我們可以看到,我們首先得到framebuffer環(huán)境變量,然后我們打開了他,并返回了她的文件描述符。然后我們利用 mmap 功能將幀緩沖映射到用戶進(jìn)程空間形成用戶能操作的顯存對顯存進(jìn)行讀寫操作。通過程序我們看到mmap把framebufer隱射到psd的地址空間,然后我們可以看到在microwindows所有的有關(guān)圖像處理的函數(shù)都是在psd申請的內(nèi)存內(nèi)進(jìn)行,,就這樣microwindows通過寫這片內(nèi)存來實現(xiàn)對framebuffer的使用,而對framebuffer的任何操作馬上就反映屏幕上,就這樣microwindows用framebuffer來實現(xiàn)了所有的圖像顯示,就這樣microwindows讓我們看到了艷麗多彩的圖形界面。
第3章 microwindows兩種繪圖區(qū)
4.1創(chuàng)建實窗口
創(chuàng)建實窗口使用GrNewWindow,當(dāng)然我們也可以調(diào)用GrNewInputWindow 函數(shù)
創(chuàng)間只輸入(input-only)的窗口,這些函數(shù)可以指定窗口的邊界和顏色,函數(shù)的返回值是窗口的ID,此ID可以被后來的圖形上下文和窗口操作函數(shù)使用,然后調(diào)用GrMapWindow 函數(shù)顯示窗口,我們也可以調(diào)用GrUnmapWindow 函數(shù)隱藏窗口。窗口的顯
示的前提示需要它的所有祖先窗口可視。再后來就是繪制窗口了,繪制窗口需要窗口號和圖形上下文作參數(shù),我們已經(jīng)在前面生成。再后來就是我們可以看到絢麗的圖像窗口了。
4.2 虛窗口(microwindows中的offscreen)
Microwindows也支持一種從來不在屏幕上顯示的窗口,即像素映射(pixmap)。像素映射窗口有時也稱作虛窗口(offscreen),虛窗口不能在屏幕上顯示出來,但是可以使用GrCopyArea函數(shù)復(fù)制到別的窗口。有時在expose events期間,CPU太忙而無法保存顯示窗口的內(nèi)容,普通窗口在被遮掩時又從不保存它們的內(nèi)容,這時就可以使用像素映射。用GrNewPixmap函數(shù)可以生成一個像素映射。系統(tǒng)使用malloc分配的內(nèi)存地址代替用mmap分配的物理framebuffer地址。在系統(tǒng)不使用實際物理內(nèi)存地址的情況下(X或MS窗口),必須寫兩套函數(shù),一套用于圖形系統(tǒng)硬件,一套用于內(nèi)存地址。另外,需要知道在兩種格式之間的復(fù)制方法。位的blitting函數(shù)必須要快。查看fblin8.c和mempl4.c文件可以找到支持兩種顯示硬件類型的例子。我做過一個對于microwindows圖形引擎的修改,就是自己在devimage.c自己定義了一套繪圖函數(shù)和驅(qū)動,然后把圖像畫在自己申請的內(nèi)存中,然后把畫好后的圖像用GrArea拷貝到一個正在顯示的圖像界面上,結(jié)果獲得了成功,我想這是我對在屏幕外作圖最深刻的體會把。
第4章microwidows對圖片的解析
microwindows支持多種圖片格式,包括GIF,JPEG,BMP,PNG,XPM,PBM,PPM等等,那么microwindows是怎么對這些圖像進(jìn)行選擇和解碼的呢?位圖是現(xiàn)在比較常見的圖像格式,那么我們就主要說一下microwindows對位圖的解碼過程。
3.1位圖的格式
bmp文件大體上分成四個部分:
第一部分:為位圖文件頭BITMAPFILEHEADER,是一個結(jié)構(gòu),其定義如下:
typedefstructtagBITMAPFILEHEADER{
WORD bfType;
DWORD bfSize;
WORD bfReserved1;
WORD bfReserved2;
DWORD bfOffBits;
} BITMAPFILEHEADER;
這個結(jié)構(gòu)的長度是固定的,為14個字節(jié)(WORD為無符號16位整數(shù),DWORD為無符號32位整數(shù)),各個域的說明如下:
bfType
指定文件類型,必須是0x424D,即字符串"BM",也就是說所有.bmp文件的頭兩個字節(jié)都是"BM"
bfSize
指定文件大小,包括這14個字節(jié)
bfReserved1,bfReserved2
為保留字,不用考慮
bfOffBits
為從文件頭到實際的位圖數(shù)據(jù)的偏移字節(jié)數(shù),即圖3中前三個部分的長度之和。
第二部分:為位圖信息頭BITMAPINFOHEADER,也是一個結(jié)構(gòu),其定義如下:
typedef struct tagBITMAPINFOHEADER{
DWORD biSize;
LONG biWidth;
LONG biHeight;
WORD biPlanes;
WORD biBitCount;
DWORD biCompression;
DWORD biSizeImage;
LONG biXPelsPerMeter;
LONG biYPelsPerMeter;
DWORD biClrUsed;
DWORD biClrImportant;
} BITMAPINFOHEADER; 這個結(jié)構(gòu)的長度是固定的,為40個字節(jié)(WORD為無符號16位整數(shù),DWORD無符號32位整數(shù),LONG為32位整數(shù)),各個域的說明如下:
biSize
指定這個結(jié)構(gòu)的長度,為40
biWidth
指定圖象的寬度,單位是象素
biHeight
指定圖象的高度,單位是象素
biPlanes
必須是1,不用考慮
biBitCount
指定表示顏色時要用到的位數(shù),常用的值為1(黑白二色圖),4(16色圖),8(256色),24(真彩色圖)(新的.bmp格式支持32位色,這里就不做討論了)。
biCompression
指定位圖是否壓縮,有效的值為BI_RGB,BI_RLE8,BI_RLE4,BI_BITFIELDS(都是一些Windows定義好的常量)。要說明的是,Windows位圖可以采用RLE4,和RLE8的壓縮格式,但用的不多。我們今后所討論的只有第一種不壓縮的情況,即biCompression為 BI_RGB的情況。
biSizeImage
指定實際的位圖數(shù)據(jù)占用的字節(jié)數(shù),其實也可以從以下的公式中計算出來:
biSizeImage=biWidth'*biHeight
要注意的是:上述公式中的biWidth'必須是4的整倍數(shù)(所以不是biWidth,而是biWidth',表示大于或等于biWidth的,離4最近的整倍數(shù)。舉個例子,如果biWidth=240,則biWidth'=240;如果biWidth=241,biWidth'=244)如果 biCompression為BI_RGB,則該項可能為零
biXPelsPerMeter
指定目標(biāo)設(shè)備的水平分辨率,單位是每米的象素個數(shù)。
biYPelsPerMeter
指定目標(biāo)設(shè)備的垂直分辨率,單位同上。
biClrUsed
指定本圖象實際用到的顏色數(shù),如果該值為零,則用到的顏色數(shù)為2的biBitCount次方。
biClrImportant
指定本圖象中重要的顏色數(shù),如果該值為零,則認(rèn)為所有的顏色都是重要的。
第三部分:為調(diào)色板(Palette),當(dāng)然,這里是對那些需要調(diào)色板的位圖文件而言的。有些位圖,如真彩色圖,前面已經(jīng)講過,是不需要調(diào)色板的,BITMAPINFOHEADER后直接是位圖數(shù)據(jù)。
調(diào)色板實際上是一個數(shù)組,共有biClrUsed個元素(如果該值為零,則有2的biBitCount次方個元素)。數(shù)組中每個元素的類型是一個RGBQUAD結(jié)構(gòu),占4個字節(jié),其定義如下:
typedef struct tagRGBQUAD{
BYTE rgbBlue; //該顏色的藍(lán)色分量
BYTE rgbGreen; //該顏色的綠色分量
BYTE rgbRed; //該顏色的紅色分量
BYTE rgbReserved; //保留值
} RGBQUAD;
第四部分:就是實際的圖象數(shù)據(jù)了。對于用到調(diào)色板的位圖,圖象數(shù)據(jù)就是該像素顏在調(diào)色板中的索引值,對于真彩色圖,圖象數(shù)據(jù)就是實際的R,G,B值。下面就2色,16色,256色位圖和真彩色位圖分別介紹。對于2色位圖,用1位就可以表示該像素的顏色(一般0表示黑,1表示白),所以一個字節(jié)可以表示8個像素對于16色位圖,用4位可以表示一個像素的顏色,所以一個字節(jié)可以表示2個像素。對于256色位圖,一個字節(jié)剛好可以表示1個像素。對于真彩色圖,三個字節(jié)才能表示1個像素。
3.2 microwindows對位圖的解碼
microwidnows對位圖的解碼是在/microwindows-0.90/src/engine/devimage.c文件中的loadBmp函數(shù)中進(jìn)行的,下面我們就詳細(xì)分析這個函數(shù),激動人心的時候終于來了。
下面這個函數(shù)就是bmp的解碼函數(shù):
1.static int
2.LoadBMP(buffer_t *src, PMWIMAGEHDR pimage)
3.{
4. int h, i, compression;
5. int headsize;
6. MWUCHAR *imagebits;
7. BMPFILEHEAD bmpf;
8. BMPINFOHEAD bmpi;
9. BMPCOREHEAD bmpc;
10. MWUCHAR headbuffer[INFOHEADSIZE];
11.
12. bseek(src, 0, SEEK_SET);
13.
14. pimage->imagebits = NULL;
15. pimage->palette = NULL;
16.
17. /* read BMP file header*/
18. if (bread(src, &headbuffer, FILEHEADSIZE) != FILEHEADSIZE)
19. return(0);
20.
21. bmpf.bfType[0] = headbuffer[0];
22. bmpf.bfType[1] = headbuffer[1];
23.
24. /* Is it really a bmp file ? */
25. if (*(WORD*)&bmpf.bfType[0] != wswap(0x4D42)) /* 'BM' */
26. return 0; /* not bmp image*/
#if 0
通過18行我們知道FILEHEADSIZE=14,25行讀出數(shù)組的前兩個字節(jié),驗證是否是0x424D,如果是就是位圖,若不是位圖,退出。
#endif
27.
28. /*bmpf.bfSize = dwswap(dwread(&headbuffer[2]));*/
29. bmpf.bfOffBits = dwswap(dwread(&headbuffer[10]));
30. /* Read remaining header size */
31. if (bread(src,&headsize,sizeof(DWORD)) != sizeof(DWORD))
32. return 0; /* not bmp image*/
33. headsize = dwswap(headsize);
34.
35. /* might be windows or os/2 header */
36.
37. if(headsize == COREHEADSIZE) {
#if 0
如果headsize=12證明這張位圖是有os/2系統(tǒng)產(chǎn)生的,OS/2是Operating System 2的縮寫,意思為第二代的操作系統(tǒng)。在DOS于PC上的巨大成功后,以及GUI圖形化界面的潮流影響下,IBM和Microsoft共同研制和推出了OS/2這一當(dāng)時先進(jìn)的個人電腦上的新一代操作系統(tǒng)由于現(xiàn)在已不常見,我就不在敘述。
#endif
38.
39. /* read os/2 header */
40. if(bread(src, &headbuffer, COREHEADSIZE-sizeof(DWORD)) !=
41. COREHEADSIZE-sizeof(DWORD))
42. return 0; /* not bmp image*/
43.
44. /* Get data */
45. bmpc.bcWidth = wswap(*(WORD*)&headbuffer[0]);
46. bmpc.bcHeight = wswap(*(WORD*)&headbuffer[2]);
47. bmpc.bcPlanes = wswap(*(WORD*)&headbuffer[4]);
48. bmpc.bcBitCount = wswap(*(WORD*)&headbuffer[6]);
49.
50. pimage->width = (int)bmpc.bcWidth;
51. pimage->height = (int)bmpc.bcHeight;
52. pimage->bpp = bmpc.bcBitCount;
53.
54. if (pimage->bpp <= 8)
55. pimage->palsize = 1 << pimage->bpp;
56. else pimage->palsize = 0;
57. compression = BI_RGB;
58. } else {
59. /* read windows header */
60. if(bread(src, &headbuffer, INFOHEADSIZE-sizeof(DWORD)) !=
61. INFOHEADSIZE-sizeof(DWORD))
62. return 0; /* not bmp image*/
63.
64. /* Get data */
#if 0
把位圖信息頭數(shù)據(jù)讀取到bmpi中。
dswap的作用就是把little-endian格式轉(zhuǎn)換成主cpu的數(shù)據(jù)存放形式,little-endian是表示計算機字節(jié)順序的格式,所謂的字節(jié)順序指的是長度跨越多個字節(jié)的數(shù)據(jù)的存放形式. 比如我們讀書的習(xí)慣是從左到右的方式而cpu讀數(shù)的方式是從右到左的方式,所以我們在用計算機處理數(shù)據(jù)時,就必須發(fā)他轉(zhuǎn)換成正確的cpu讀數(shù)方式。
#endif
65. bmpi.BiWidth = dwswap(*(DWORD*)&headbuffer[0]);
66. bmpi.BiHeight = dwswap(*(DWORD*)&headbuffer[4]);
67. bmpi.BiPlanes = wswap(*(WORD*)&headbuffer[8]);
68. bmpi.BiBitCount = wswap(*(WORD*)&headbuffer[10]);
69. bmpi.BiCompression = dwswap(*(DWORD*)&headbuffer[12]);
70. bmpi.BiSizeImage = dwswap(*(DWORD*)&headbuffer[16]);
71. bmpi.BiXpelsPerMeter = dwswap(*(DWORD*)&headbuffer[20]);
72. bmpi.BiYpelsPerMeter = dwswap(*(DWORD*)&headbuffer[24]);
73. bmpi.BiClrUsed = dwswap(*(DWORD*)&headbuffer[28]);
74. bmpi.BiClrImportant = dwswap(*(DWORD*)&headbuffer[32]);
75.
76. pimage->width = (int)bmpi.BiWidth;
77. pimage->height = (int)bmpi.BiHeight;
78. pimage->bpp = bmpi.BiBitCount;
79. pimage->palsize = (int)bmpi.BiClrUsed;
80. if (pimage->palsize > 256)
81. pimage->palsize = 0;
82. else if(pimage->palsize == 0 && pimage->bpp <= 8)
83. pimage->palsize = 1 << pimage->bpp;
84. compression = bmpi.BiCompression;
85. }
86. pimage->compression = MWIMAGE_BGR; /* right side up, BGR order*/
87. pimage->planes = 1;
88.
89. /* currently only 1, 4, 8 and 24 bpp bitmaps*/
90. if(pimage->bpp > 8 && pimage->bpp != 24) {
91. EPRINTF("LoadBMP: image bpp not 1, 4, 8 or 24/n");
92. return 2; /* image loading error*/
93. }
94.
95. /* compute byte line size and bytes per pixel*/
96. ComputePitch(pimage->bpp, pimage->width, &pimage->pitch,
97. &pimage->bytesperpixel);
#if 0
計算行距pitch:如果你的應(yīng)用程序要寫視頻RAM,內(nèi)存中的位圖并不需要占據(jù)連續(xù)的內(nèi)存塊。在這種情況下,一條線的width和pitch含義是不同的。width是指內(nèi)存中位圖的一條線的開始和結(jié)束位置的內(nèi)存地址之差。這個距離只代表了內(nèi)存中位圖的寬度,它不包括位圖中到達(dá)下一條線開始位置所需要的任何額外的內(nèi)存。 pitch是指內(nèi)存中位圖的一條線到下一條線開始位置的內(nèi)存地址之差。
#endif
98.
99. /* Allocate image */
100. if( (pimage->imagebits = malloc(pimage->pitch*pimage->height)) == NULL)
101. goto err;
//為位圖分配內(nèi)存,注意:不是pimage->height *pimage ->height;
102. if( (pimage->palette = malloc(256*sizeof(MWPALENTRY))) == NULL)
103. goto err;
104.
105. /* get colormap*/
//生成顏色查找表
106. if(pimage->bpp <= 8) {
107. for(i=0; i<pimage->palsize; i++) {
108. pimage->palette[i].b = bgetc(src);
109. pimage->palette[i].g = bgetc(src);
110. pimage->palette[i].r = bgetc(src);
111. if(headsize != COREHEADSIZE)
112. bgetc(src);
113. }
114. }
115.
116. /* decode image data*/
117. bseek(src, bmpf.bfOffBits, SEEK_SET);
118.
119. h = pimage->height;
120. /* For every row ... */
//從右下角開始逐行向上讀取數(shù)據(jù)
121. while (--h >= 0) {
122. /* turn image rightside up*/
// 把imagebits指針移動到內(nèi)存的右下角
123. imagebits = pimage->imagebits + h*pimage->pitch;
124.
125. /* Get row data from file */
//解壓縮數(shù)據(jù),BI_RLE8,BI_RLE4均為數(shù)據(jù)壓縮格式
126. if(compression == BI_RLE8) {
127. if(!DecodeRLE8(imagebits, src))
128. break;
129. } else if(compression == BI_RLE4) {
130. if(!DecodeRLE4(imagebits, src))
131. break;
132. } else {
133. if(bread(src, imagebits, pimage->pitch) !=
134. pimage->pitch)
135. goto err;
136. }
137. }
138. return 1; /* bmp image ok*/
139.
140.err:
141. EPRINTF("LoadBMP: image loading error/n");
142. if(pimage->imagebits)
143. free(pimage->imagebits);
144. if(pimage->palette)
145. free(pimage->palette);
146. return 2; /* bmp image error*/
147.}
總結(jié)
以上是生活随笔為你收集整理的microwindows位图解析的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: es6(五):函数的扩展
- 下一篇: 走向无后端的系统开发实践:CRUD自动化