microwindows位图解析
From: http://blog.csdn.net/bisword/article/details/2740054
第1章 microwinodows圖形顯示框架
1.1 microwindows體系結構
Microwindows 采用了分層結構設計方法,其層次結構如下圖所示。同時, 這里也列出 Microwindows 源代碼目錄樹下的主要目錄結構,以便于對照參考。
microwindows --bin
--Configs
nano-X /win32 API --demos
--drivers
engine --engine
色彩控制|blitting|區域剪裁|clipping|圖形繪制 microwindows-0.90 --fonts
--includes --lib
顯示驅動 | 鍵盤驅動 | 鼠標/觸摸屏驅動 --mwin
--nanox
圖1 microwindows體系結構
Microwindows 提 供 兩 種 類 型 的 API , 一 種 叫 Microwindows API,是與win32/WinCE GDI 相兼容的 API 接口,另一種叫 Nano-X API,是與 Linux 上著名的 X Window 相兼容的 API。實現 Microwindows API 的程序放置在 mwin 目錄下,實現 Nano-X API 的程序放置在 nanox 目錄下。
API層之下是設備無關的圖形引擎層,該層負責底層繪圖原語,實現各種圖形繪制, 各種圖像格式處理,字體處理等功能。該層的程序主要放置在 engine 和 fonts 目錄下。
在最下層是設備驅動層,該層為各類設備提供驅動程序,目前包括顯示/觸摸屏,鼠標,鍵盤等設備的驅動程序,驅動層負責與物理設備進行交互通信,該層的程序都放置在 drivers 目錄下。
1.2 引擎層
1.2.1 引擎層剖析
圖形引擎層在 Microwindows 中起著承上啟下的作用,既對下層設備驅動的功能實行封裝,又為上層應用編程接口 API 提供服務,系統核心圖形繪制功能都是在該層實現的。由于我們這次我們主要講解的是關于圖像處理方面的知識,所以其他不相關的我們暫且不再涉及。以下我們從,圖形上下文,繪圖區域,裁剪,等方面介紹圖形引擎層的功能與實現,并先列舉 engine 目錄下的關于圖像處理主要文件及其完成的功能以供參考:
Devdraw.c:主要的圖形繪制函數,包括調色板操作,背景繪制,畫線,畫圓,多邊型與填充,位操作及圖形的放大和縮小等。
Devclip.c : 剪裁操作。
Devrgn.c :實現圖形的布爾操作。
Devpalx.c :處理 1,2,4,8 位調色板的映射。
Devpalgray4.c:處理 4 級灰度映射。
Devimage.c :處理對 GIP,BMP,JPEG,PPM,PNG,TIFF 等圖形格式的顯示操作。
Microwindows的中間層是圖形引擎層(也稱為設備與平臺無關層),圖形引擎層為應用層提供了一系列的繪圖函數,因為Microwindows系統中最核心的圖形函數是圖形引擎層通過調用下層的硬件設備驅動程序實現的,所以該層與硬件無關。在microwindows的繪圖過程中用戶應用程序通常不直接調用引擎層的繪圖函數,而是通過調用最上層所提供的編程接口API,來實現圖像的繪制。這些繪制函數的命名遵循 GdXXX()的模式,第一個參數是 SCREENDEVICE 結構的指針,在 device.h 中定義,描述了顯示器的底層特性,是顯示設備驅動程序的接口。SCREENDEVICE 結構中包含了顯示設備的物理特性,比如橫向和縱向的大小,顯示模式,色彩,顯示內存的大小等屬性,此外該結構中也包含了各種和屏幕設備相關的操作的函數指針,所有的屏幕坐標都是COORD類型的,以設備坐標的方式指明,即對屏幕左上角的位移。顏色總是以RGB COLORVAL值的方式傳送給圖形引擎。他們可能在轉換為調色板的索引值后,作為PIXELVAL值被傳送給顯示硬件。在下面的底層驅動的章節中我會詳細介紹他們,下面我們對圖形引擎中的用到的概念進行解釋。
1.2.1.1 圖形上下文
Nano-X 中使用圖形上下文描述繪圖屬性。要在屏幕上繪圖,需要先調用GrNewGC()函數分配一個圖形上下文數據結構,然后返回數據結構的 ID 值。圖形上下文實際上標識了一個 GR_GC_INFO 結構體,如下所示,這個結構體包含了前景,背景,字體,繪圖模式和繪圖區域等信息,設置圖形上下文時使用函數進行操作,而不是對結構體直接操作。
typedef struct {
GR_GC_ID gcid; /*圖形上下文ID */
int mode; /*繪畫模式*/
GR_REGION_ID region; /*繪畫區域*/
int xoff; /*x偏移量(以繪畫區域的左上角為參照)*/
int yoff; /*y偏移量*/
GR_FONT_ID font; /*字體大小設置*/
GR_COLOR foreground; /*前景色RGB值或像素值*/
GR_COLOR background; /* 背景色RGB值或像素值 */
GR_BOOL fgispixelval; /*如果前景色為像素值則定義為TRUE?*/
GR_BOOL bgispixelval; /* 如果背景色為像素值則定義為TRUE */
GR_BOOL usebackground; /*位圖作為背景 */
GR_BOOL exposure; /* 向 GrCopyArea 發出顯示事件*/
} GR_GC_INFO;
1.2.1.2 繪圖區域
矩形區域被用來描述屏幕上點的集合。簡單的情況下,矩形區域可以使用一個長方形來描述,但是在復雜情況下可能要使用復雜的數據結構描述。Microwindows 中繪圖區域使用互不重疊的矩形區域表示,因此對于一個不規則的凸邊形要使用多個不重疊的矩形拼和。
1.2.1.3裁剪
Microwindows 中裁剪和以上提及的繪圖區域有著密切的關系。任何時候, 圖形引擎都有一個繪圖區域,這個區域是由一系列的矩形組成的,所有對圖形的操作都是在這個區域進行。系統中實現裁剪有兩種方法,都使用了GdSetClipRects()函數將裁剪區域傳入圖形引擎, 此后所有的繪圖函數都要先判斷繪圖區域是否能被繪制,然后才進行繪制。
1.3底層驅動
人機交互系統必然都包含輸入/輸出設備,Microwindows 中控制的設備包括顯示器/屏輸出設備和鼠標/鍵盤/觸摸屏等輸入設備。設備驅動程序的接口函數在 device.h 中定義,至少包含一個屏幕驅動程序,一個鼠標驅動程序和一個鍵盤驅動程序。圖形引擎層能夠直接調用這些驅動程序進行相應的硬件設備操作, 從而實現設備無關性, 使用這樣的層次結構能保證添加其他的硬件設備時不影響整個系統的正常工作。
1.3.1屏幕驅動
microwindows屏幕驅動是基于Linux內核中framebuffer,這要求在編譯內核的時候選擇支持framebuffer編譯參數選項。它是通過fd=open(env="/dev/fb0")打開,用SCREENDEVICE的指針PSD指向這片顯存,然后對這片顯存根據屏幕的不同位色設置情況為中間引擎層提供相應的圖形操作支持,包括畫點線、圖片顯示、屏幕拷貝以及中西文字的顯示等等。由于microwindows自身的特性他被被設計成能夠極其容易的向Microwindows中移植新硬件,因此必須提供一些訪問硬件的入口點,同時提供其他的函數保證只使用小部分的核心函數集就可以完成此任務。例如:屏幕驅動程序必須實現ReadPixel,DrawPixel,DrawHorzline和DrawVertLine。這些函數在顯存中讀取或寫入一個像素,或畫一條水平或垂直的線。位圖傳送允許Microwindows執行屏幕外的作圖。Microwindows允許任何能在屏幕上進行的圖形操作在屏幕外進行,然后再將圖形傳送到物理顯存。下面我們就介紹顯示設備驅動使用的典型數據結構,參考該結構可以幫助了解顯示設備的基本功能。
顯示驅動接口數據結構:
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 :當處于平面顯示模式時,planes 用于記錄所使用的平面數,如平面模式相對的時
線性模式,此時該變量沒有意義。通常將其置為0。
bpp :表示每個像素所使用的比特數,可以為1,2,4,8,15,16,24,32。
linelen :對與1,2,4,8比特每像素模式,它表示一行像素使用的字節數,對于大于8比
特每像素模式,它表示一行的像素總數。
size :表示該顯示模式下該設備使用的內存數。linelen 和 size 的存在主要是為了方便為內
存屏幕設備分配內存。
gr_foreground 和 gr_background :表示該內存屏幕的前景顏色和背景顏色,主要被一些
GDI 函數使用。
gr_mode :說明如何將像素畫到屏幕上,可選值為:MODE_SET MODE_XOR MODE_OR
MODE_AND MODE_MAX,比較常用的是MODE_SET和MODE_XOR
flags :該屏幕設備的一些選項,比較重要的是 PSF_MEMORY 標志,表示該屏幕設備代
表物理屏幕設備還是一個內存屏幕設備。
addr :每個屏幕設備都有一塊內存空間用來作為存儲像素。addr 變量記錄了這個空間的起
始地址。
下面的函數是屏幕驅動的接口函數,我將會在下面驅動接口中解析他們,在這里就不再介紹。
1.3.2驅動接口
在上面的文章中我們看到這樣一句話:顯示驅動是整個系統中最為復雜的部分,但是驅動程序的復雜性卻換來了移植的方便性,microwindows是怎么讓復雜的顯示驅動來方便移動呢?就是因為系統的顯示驅動程序里只有幾個接口和硬件設備直接交互,其他程序提供核心的繪圖操作比如讀取像素,繪制像素,繪制更復雜的圖形等,這些繪圖操作對系統映射的顯存進行操作,讀寫像素點的信息。舉個通俗的例子,現在的轎車和以前的轎車在和剛開始發明的時候的駕駛方式幾乎沒有什么改變,都是離合,油門,方向盤,這個固定模式就好比我們的接口,他就是這樣,不管你的車性能多好,多高級,都是內部的事,駕駛方式都是一樣的。來讓我們來看看microwindows的離合,油門,方向盤。在microwindows里們不論是畫多少位圖像的驅動我們都有統一的接口,都有統一的功能函數。下們就是microwindows驅動的接口,如果有一天我們中的那位發明一個microwidnows沒有的圖像格式,如果還要用microwindows的話,那么我們就要自己來寫驅動了,不過不要頭大,我們的驅動中只要包括下面接口中的函數就可以了,這就是microwindows的馬克思理論性,永遠沒有盡頭,永遠可以延伸和擴展。
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;
下面介紹各接口函數:
DrawPixel(PSD psd, int x, int y, gfx_pixel c):
功能:在給定的地址上畫一個像素點。
參數介紹:psd在這里我們對psd的應用就是psd的基地址,x,y就是該點相對于基址的偏移量他的算法是這樣的。addr = x * pitch + y ,addr就是該點的所要畫的位置。gfx_pixelc就是傳遞過來的像素值,該函數解析后畫到addr這個位置。絢爛多彩的圖像就是靠勤勞的microwindows這樣一點一點畫出來了。
ReadPixel(PSD psd, int x, int y):
功能:在給定的地址上讀出一個像素值。
參數介紹:psd在該函數中的用處還是他所指向的顯存的基地址,x,y就是該點相對于基址的偏移量,他的算法和上面的方式是相同的,然后就是該函數把該點像素解析后返回該像素的RGB值。
DrawHorzLine(PSD psd,MWCOORD x1,MWCOORD x2,MWCOORD y,
MWPIXELVAL c):
功能:在兩點之間畫橫線。
參數介紹:psd的作用還是向程序提供基地址,雖然該函數有三個坐標數據,其實他代表的是兩個坐標點,(x1,y),(x2,y).然后在y的位置從x1向x2從左到右畫線。c就是畫線時的填充像素了。
DrawVertLine(PSD psd,MWCOORD x,MWCOORD y1,MWCOORD y2,
MWPIXELVAL c):
功能:在兩點之間畫豎線。
參數介紹:psd作用還是提供繪圖的基地址。x,y1,y2的意思是在(x,y1)(x,y2)兩點見畫豎線。
c的作用還是畫線時的像素填充值。提醒:不論是畫豎線還是橫線,我們的microwindows都沒有偷懶,把最后一點也畫了,也就是我們的線是有頭有尾是完整的。
FillRect(PSD psd,MWCOORD x1,MWCOORD y1,MWCOORD x2,MWCOORD y2,
MWPIXELVAL c):
功能:填充一個矩形,也就是畫一個實心的矩形。
參數介紹:聰明的我們也許看了上兩個這個就不用解釋了把,是的和你認為的一樣,就是在x1,y1,x2,y2的坐標范圍內用c的像素值來填充。
PutHLine,GetHLine,PutVLine,GetVLine,PutBox,GetBox,PutBoxMask
Get* 函數用于從屏幕拷貝像素到一塊內存區,而Put*函數用于將存放于內存區的像素畫到屏幕上。PutBoxMask 與PutBox的唯一區別是要畫的像素如果是白色,就不會被畫到屏幕上,從而達到一種透明的效果。
Blit,CopyBox
Blit 用于在不同的屏幕設備(物理的或者內存的)之間拷貝一塊像素點,CopyBox則用于在同一屏幕上實現區域像素的拷貝。如果使用的是線性模式,Blit的實現非常簡單,直接memcpy 就可以了,而CopyBox 為了防止覆蓋問題,必須根據不同的情況,采用不同的拷貝方式,比如從底到頂底拷貝,當新老位置在同一水平位置并且重復時,則需要利用緩沖間接拷貝。如果使用平面顯示模式,這里就比較復雜了。因為內存設備總是采用線性模式的,所以就要判斷是物理設備還是內存設備,再分別處理。這也大大地增加了fbvga16 實現的代碼。
1.3.3驅動選擇
我們知道,圖形顯示有個顯示模式的概念,一個像素可以用一位比特表示,也可以用2,4,8,15,16,24,32個比特表示,另外,VGA16標準模式使用平面圖形模式,而VESA2.0使用的是線性圖形模式。所以即使是同樣基于Framebuffer 的驅動,不同的模式也要使用不同的驅動函數:那么microwidnows是怎樣選擇驅動的呢。寫到這里我不禁要感慨一下,今天看到這篇文章的人是多么的幸福,有人已經為你總結好了,可我可是一天幾行,請教無數善良的人們才知道的。
我說的這個內容是在microwindows/src/nanox/srvmain.c中,當我們啟動服務端后microwindows的初始化也就隨之開始了,通過GrOpenScreen我們來到了/microwindows/src/drivers/scr_fb.c中的fb_open內部,我們就是通過select_fb_subdriver來進行選擇,剛才在fb_open屏幕驅動已經對psd進行了初始化,他的過程是這樣的首先我們把framebuffer映射到我們申請的地址,然后通過FrameBuffer 中的ioctl函數
(下面有詳細解釋),framebuffer設備提供了若干 ioctl 命令,通過這些命令,可以獲得顯示設備的一些固定信息(比如顯示內存大小)、與顯示模式相關的可變信息(比如分辨率、象素結構、每掃描線的字節寬度),以及偽彩色模式下的調色板信息等等。然后microwindox根據ioctl函數返回的屏幕對psd進行初始化,然后根據psd->bpp的不同讓driver指針指向不同的驅動地址,來選擇對應的驅動,也就說驅動的選擇是microwindows根據framebuffer的返回值來選擇對應的驅動。就這樣microwindows草船借了箭.
第2章 microwindows基于framebuffer顯像原理
2.1 framebuffer簡介
Framebuffer機制模仿顯卡的功能,將顯卡硬件結構抽象掉,可以通過 Framebuffer的讀寫直接對顯存進行操作。用戶可以將Framebuffer看成是顯示內存的一個映像,將其映射到進程地址空間之后,就可以直接進行讀寫操作,而寫操作可以立即反應在屏幕上。FrameBuffer 設備還提供了若干 ioctl 命令,通過這些命令(上面的驅動選擇中用到)可以獲得顯示設備的一些固定信息(比如顯示內存大小)、與顯示模式相關的可變信息(比如分辨率、象素結構、每掃描線的字節寬度),以及偽彩色模式下的調色板信息。
2.2 framebuffer在microwindows圖形解析中的應用
如果在microwidnows使用framebuffer我們首先會在microwidnows的預編譯選項中,選中framebuffer,然后運行microwidnow服務端在初始化過程中打開幀緩沖設備,過程如下:(microwindows-0.90/src/nanox/srvmain.c中的GsInitialize/GdOpenScreen/scrdev/fbopen()函數),
if(!(env = getenv("FRAMEBUFFER")))
{
env = "/dev/fb0";
}
fb = open(env, O_RDWR);
從上面代碼我們可以看到,我們首先得到framebuffer環境變量,然后我們打開了他,并返回了她的文件描述符。然后我們利用 mmap 功能將幀緩沖映射到用戶進程空間形成用戶能操作的顯存對顯存進行讀寫操作。通過程序我們看到mmap把framebufer隱射到psd的地址空間,然后我們可以看到在microwindows所有的有關圖像處理的函數都是在psd申請的內存內進行,,就這樣microwindows通過寫這片內存來實現對framebuffer的使用,而對framebuffer的任何操作馬上就反映屏幕上,就這樣microwindows用framebuffer來實現了所有的圖像顯示,就這樣microwindows讓我們看到了艷麗多彩的圖形界面。
第3章 microwindows兩種繪圖區
4.1創建實窗口
創建實窗口使用GrNewWindow,當然我們也可以調用GrNewInputWindow 函數
創間只輸入(input-only)的窗口,這些函數可以指定窗口的邊界和顏色,函數的返回值是窗口的ID,此ID可以被后來的圖形上下文和窗口操作函數使用,然后調用GrMapWindow 函數顯示窗口,我們也可以調用GrUnmapWindow 函數隱藏窗口。窗口的顯
示的前提示需要它的所有祖先窗口可視。再后來就是繪制窗口了,繪制窗口需要窗口號和圖形上下文作參數,我們已經在前面生成。再后來就是我們可以看到絢麗的圖像窗口了。
4.2 虛窗口(microwindows中的offscreen)
Microwindows也支持一種從來不在屏幕上顯示的窗口,即像素映射(pixmap)。像素映射窗口有時也稱作虛窗口(offscreen),虛窗口不能在屏幕上顯示出來,但是可以使用GrCopyArea函數復制到別的窗口。有時在expose events期間,CPU太忙而無法保存顯示窗口的內容,普通窗口在被遮掩時又從不保存它們的內容,這時就可以使用像素映射。用GrNewPixmap函數可以生成一個像素映射。系統使用malloc分配的內存地址代替用mmap分配的物理framebuffer地址。在系統不使用實際物理內存地址的情況下(X或MS窗口),必須寫兩套函數,一套用于圖形系統硬件,一套用于內存地址。另外,需要知道在兩種格式之間的復制方法。位的blitting函數必須要快。查看fblin8.c和mempl4.c文件可以找到支持兩種顯示硬件類型的例子。我做過一個對于microwindows圖形引擎的修改,就是自己在devimage.c自己定義了一套繪圖函數和驅動,然后把圖像畫在自己申請的內存中,然后把畫好后的圖像用GrArea拷貝到一個正在顯示的圖像界面上,結果獲得了成功,我想這是我對在屏幕外作圖最深刻的體會把。
第4章microwidows對圖片的解析
microwindows支持多種圖片格式,包括GIF,JPEG,BMP,PNG,XPM,PBM,PPM等等,那么microwindows是怎么對這些圖像進行選擇和解碼的呢?位圖是現在比較常見的圖像格式,那么我們就主要說一下microwindows對位圖的解碼過程。
3.1位圖的格式
bmp文件大體上分成四個部分:
第一部分:為位圖文件頭BITMAPFILEHEADER,是一個結構,其定義如下:
typedefstructtagBITMAPFILEHEADER{
WORD bfType;
DWORD bfSize;
WORD bfReserved1;
WORD bfReserved2;
DWORD bfOffBits;
} BITMAPFILEHEADER;
這個結構的長度是固定的,為14個字節(WORD為無符號16位整數,DWORD為無符號32位整數),各個域的說明如下:
bfType
指定文件類型,必須是0x424D,即字符串"BM",也就是說所有.bmp文件的頭兩個字節都是"BM"
bfSize
指定文件大小,包括這14個字節
bfReserved1,bfReserved2
為保留字,不用考慮
bfOffBits
為從文件頭到實際的位圖數據的偏移字節數,即圖3中前三個部分的長度之和。
第二部分:為位圖信息頭BITMAPINFOHEADER,也是一個結構,其定義如下:
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; 這個結構的長度是固定的,為40個字節(WORD為無符號16位整數,DWORD無符號32位整數,LONG為32位整數),各個域的說明如下:
biSize
指定這個結構的長度,為40
biWidth
指定圖象的寬度,單位是象素
biHeight
指定圖象的高度,單位是象素
biPlanes
必須是1,不用考慮
biBitCount
指定表示顏色時要用到的位數,常用的值為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
指定實際的位圖數據占用的字節數,其實也可以從以下的公式中計算出來:
biSizeImage=biWidth'*biHeight
要注意的是:上述公式中的biWidth'必須是4的整倍數(所以不是biWidth,而是biWidth',表示大于或等于biWidth的,離4最近的整倍數。舉個例子,如果biWidth=240,則biWidth'=240;如果biWidth=241,biWidth'=244)如果 biCompression為BI_RGB,則該項可能為零
biXPelsPerMeter
指定目標設備的水平分辨率,單位是每米的象素個數。
biYPelsPerMeter
指定目標設備的垂直分辨率,單位同上。
biClrUsed
指定本圖象實際用到的顏色數,如果該值為零,則用到的顏色數為2的biBitCount次方。
biClrImportant
指定本圖象中重要的顏色數,如果該值為零,則認為所有的顏色都是重要的。
第三部分:為調色板(Palette),當然,這里是對那些需要調色板的位圖文件而言的。有些位圖,如真彩色圖,前面已經講過,是不需要調色板的,BITMAPINFOHEADER后直接是位圖數據。
調色板實際上是一個數組,共有biClrUsed個元素(如果該值為零,則有2的biBitCount次方個元素)。數組中每個元素的類型是一個RGBQUAD結構,占4個字節,其定義如下:
typedef struct tagRGBQUAD{
BYTE rgbBlue; //該顏色的藍色分量
BYTE rgbGreen; //該顏色的綠色分量
BYTE rgbRed; //該顏色的紅色分量
BYTE rgbReserved; //保留值
} RGBQUAD;
第四部分:就是實際的圖象數據了。對于用到調色板的位圖,圖象數據就是該像素顏在調色板中的索引值,對于真彩色圖,圖象數據就是實際的R,G,B值。下面就2色,16色,256色位圖和真彩色位圖分別介紹。對于2色位圖,用1位就可以表示該像素的顏色(一般0表示黑,1表示白),所以一個字節可以表示8個像素對于16色位圖,用4位可以表示一個像素的顏色,所以一個字節可以表示2個像素。對于256色位圖,一個字節剛好可以表示1個像素。對于真彩色圖,三個字節才能表示1個像素。
3.2 microwindows對位圖的解碼
microwidnows對位圖的解碼是在/microwindows-0.90/src/engine/devimage.c文件中的loadBmp函數中進行的,下面我們就詳細分析這個函數,激動人心的時候終于來了。
下面這個函數就是bmp的解碼函數:
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行讀出數組的前兩個字節,驗證是否是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系統產生的,OS/2是Operating System 2的縮寫,意思為第二代的操作系統。在DOS于PC上的巨大成功后,以及GUI圖形化界面的潮流影響下,IBM和Microsoft共同研制和推出了OS/2這一當時先進的個人電腦上的新一代操作系統由于現在已不常見,我就不在敘述。
#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
把位圖信息頭數據讀取到bmpi中。
dswap的作用就是把little-endian格式轉換成主cpu的數據存放形式,little-endian是表示計算機字節順序的格式,所謂的字節順序指的是長度跨越多個字節的數據的存放形式. 比如我們讀書的習慣是從左到右的方式而cpu讀數的方式是從右到左的方式,所以我們在用計算機處理數據時,就必須發他轉換成正確的cpu讀數方式。
#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:如果你的應用程序要寫視頻RAM,內存中的位圖并不需要占據連續的內存塊。在這種情況下,一條線的width和pitch含義是不同的。width是指內存中位圖的一條線的開始和結束位置的內存地址之差。這個距離只代表了內存中位圖的寬度,它不包括位圖中到達下一條線開始位置所需要的任何額外的內存。 pitch是指內存中位圖的一條線到下一條線開始位置的內存地址之差。
#endif
98.
99. /* Allocate image */
100. if( (pimage->imagebits = malloc(pimage->pitch*pimage->height)) == NULL)
101. goto err;
//為位圖分配內存,注意:不是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 ... */
//從右下角開始逐行向上讀取數據
121. while (--h >= 0) {
122. /* turn image rightside up*/
// 把imagebits指針移動到內存的右下角
123. imagebits = pimage->imagebits + h*pimage->pitch;
124.
125. /* Get row data from file */
//解壓縮數據,BI_RLE8,BI_RLE4均為數據壓縮格式
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.}
總結
以上是生活随笔為你收集整理的microwindows位图解析的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: es6(五):函数的扩展
- 下一篇: 一张浓缩大学生活的顶级报(转载)