#include<stdio.h>
#include<stdlib.h> //exit()
#include<dos.h> //in86()
#include<bios.h> //close()
#include<fcntl.h> //open()
#include<io.h> //lseek(),read()
#include<conio.h> //outp(),getch()
#define VGA256 0x13 //320*200 256色 顯示模式
#define TEXT_MODE 0x03 //80×25 16 色 文本模式
#define SCREEN_HEIGHT 200 //圖象高度,像素單位
#define SCREEN_WIDTH 320 //圖象寬度,像素單位
#define PALETTE_MASK 0x3c6 //調色板屏蔽寄存器端口,放入0xff可以通過調色板索引寄存器0x3c7和0x3c8訪問你希望的寄存器
#define PALETTE_REGISTER_RD 0x3c7 //讀顏色寄存器端口
#define PALETTE_REGISTER_WR 0x3c8 //寫顏色寄存器端口
#define PALETTE_DATA 0x3c9 //調色板數據寄存器端口
unsigned char far *video_buffer=(char far *)0xA0000000L;
typedef struct BMP_file
{
unsigned int bfType; //這裡恆定等於0x4D42,ASCII字符‘BM’
unsigned long bfSize; //文件大小,以字節為單位
unsigned int Reserved1; //備用,必須為0
unsigned int reserved2; //備用,必須為0
unsigned long bfOffset; //數據區在文件中的位置偏移量,以字節為單位
}bitmapfile; //文件頭結構體,14 字節
typedef struct BMP_info
{
unsigned long biSize; //位圖信息頭大小,本結構所占用字節數
unsigned long biWidth; //圖象寬度,像素單位
unsigned long biHeight; //圖象高度,像素單位
unsigned int biPlanes; //位平面樹,目標設備的級別,必須為1
unsigned int biBitCount; //單位像素的位數,表示BMP圖片的顏色位數,必須是1(雙色 ),4(16色),8(256色),24位圖(真彩色),32位圖
unsigned long biCompression; //圖片壓縮屬性,必須為:0(不壓縮),1(BI_RLE8壓縮類型)或2(BI_RLE4壓縮類型)之一
unsigned long biSizeImage; //表示圖片數據區的大小,當biBompression等於0時,這裡的值可以省略
unsigned long biXpolsPerMeter; //水平分辨率,每米像素數,可省略
unsigned long biYpelsPerMeter; //垂直分辨率,每米像素數,可省略
unsigned long biClrUsed; //表示使用了多少個顏色索引表,一般biBitCount屬性小於16才會用到,等於0時表示有2^biBitCount個顏色索引表
unsigned long biClrImportant; //表示有多少個重要的顏色,等於0時表示所有顏色都很重要
}bitmapinfo; //位圖信息頭,40 字節
typedef struct RGB_BMP_typ
{
unsigned char blue; //藍色的亮度(值范圍為0-255)
unsigned char green; //綠色的亮度(值范圍為0-255)
unsigned char red; //紅色的亮度(值范圍為0-255)
unsigned char reserved; //保留,必須為0
}RGB_BMP,*RGB_BMP_ptr; //單個像素顏色結構體,4 字節
typedef struct bmp_picture_typ
{
bitmapfile file; //位圖文件頭
bitmapinfo info; //位圖信息頭
RGB_BMP palette[256]; //位圖顏色表
} bmp_picture, *bmp_picture_ptr; //位圖非數據區結構體
void Set_BMP_Palette_Register(int index,RGB_BMP_ptr color) //設置調色板寄存器……
{
outp(PALETTE_MASK,0xff);
outp(PALETTE_REGISTER_WR,index);
outp(PALETTE_DATA,color->red>>2);
outp(PALETTE_DATA,color->green>>2);
outp(PALETTE_DATA,color->blue>>2);
}
void Check_Bmp(bmp_picture_ptr bmp_ptr) //檢測是否是BMP文件
{
if(bmp_ptr->file.bfType!=0x4d42)
{
printf("Not a BMP file!\n");
exit(1);
}
if(bmp_ptr->info.biCompression!=0)
{
printf("Can not display a compressed BMP file!\n");
exit(1);
}
if(bmp_ptr->info.biBitCount!=8)
{
printf("Not a index 16 color BMP file!\n");
exit(1);
}
}
void BMP_Load_Screen(char *bmp) //載入BMP文件並顯示……
{
int i,j,fp,n_bytes;
bmp_picture bmp256;
unsigned char line_buf[SCREEN_WIDTH];
unsigned char far *line_ptr;
if ((fp=open(bmp,O_RDONLY))==1)
{
printf("Can not open file:%s",bmp);
exit(1);
}
read(fp,&bmp256.file,sizeof(bitmapfile));
read(fp,&bmp256.info,sizeof(bitmapinfo));
Check_Bmp((bmp_picture_ptr)&bmp256);
/* lseek(fp,54,0); */
for (i=0;i<256;i++) //讀取文件顏色表數據
{
read(fp,&bmp256.palette[i].blue,1);
read(fp,&bmp256.palette[i].green,1);
read(fp,&bmp256.palette[i].red,1);
read(fp,&bmp256.palette[i].reserved,1);
}
for (i=0;i<256;i++)
Set_BMP_Palette_Register(i,(RGB_BMP_ptr)&bmp256.palette[i]); //設置調色板寄存器……
for(i=SCREEN_HEIGHT-1;i>=0;i--) //顯示位圖
{
lseek(fp,-(long)(SCREEN_HEIGHT-i)*SCREEN_WIDTH,SEEK_END); //定位讀取數據區
n_bytes=read(fp,line_buf,SCREEN_WIDTH); //讀取數據區數據
line_ptr=video_buffer+(SCREEN_HEIGHT-i-1)*SCREEN_WIDTH;
for(j=0;j<n_bytes;j++) //將數據放入視頻圖形區
line_ptr[j]=line_buf[j];
}
close(fp);
}
void Set_Video_Mode(int mode) //設置顯示模式……
{
union REGS inregs,outregs;
inregs.h.ah=0;
inregs.h.al=(unsigned char)mode;
int86(0x10,&inregs,&outregs);
}
void main()
{
Set_Video_Mode(VGA256); //設置顯示模式為320*200 256色
BMP_Load_Screen("256.bmp"); //載入要顯示位圖並顯示
getch();
Set_Video_Mode(TEXT_MODE); //設置回文本模式
}
BMP格式詳解
位圖文件頭的格式:
typedef struct{
int bfType; //bfType(2字節),這裡恆定等於&H4D42,ASCII字符'BM'
long bfSize; //文件大小,以4字節為單位
int bfReserve1; //備用
int bfReserve2; //備用
long bfoffBits; //數據區在文件中的位置偏移量
}BITMAPFILEHEADER; //文件頭結構體,14字節
typedef struct{
long bitSize; //位圖信息頭大小
long biWidth; //圖象寬度,像素單位
long biHeight; //圖象高度,像素單位
int biPlanes; //位平面樹=1
int biBitCount; //單位像素的位數,表示bmp圖片的顏色位數,即24位圖、32位圖
long biCompression; //圖片的壓縮屬性,bmp圖片是不壓縮的,等於0
long biSizeImage; //表示bmp圖片數據區的大小,當上一個屬性biCompression等於0時,這裡的值可以省略不填
long biXPlosPerMeter;//水平分辨率,可省略
long biYPlosPerMeter;//垂直分辨率,可省略
long biClrUsed; //表示使用了多少個顏色索引表,一般biBitCount屬性小於16才會用到,等於0時表示有2^biBitCount個顏色索引表
long biClrImportant; //表示有多少個重要的顏色,等於0時表示所有顏色都很重要
}BITMAPINFOHEADER; //位圖信息頭,40字節
BMP文件詳解
1、BITMAPFILEHEADER結構體
一個bmp文件以BITMAPFILEHEADER結構體開始.
第1個屬性是bfType(2字節),這裡恆定等於&H4D42。由於內存中的數據排列高位在左,低位在右,所以內存中從左往右看就顯示成(42 4D),所以在UltraEdit中頭兩個 字節顯示為(42 4D)就是這樣形成的,以後的數據都是這個特點,不再作重復說明。
第2個屬性是bfSize(4字節),表示整個bmp文件的大小,這裡等於&H000004F8=1272字節。
第3個、第4個屬性分別是bfReserved1、bfReserved2(各2字節),這裡是2個保留屬性,都為0,這裡等於&H0000、&H0000。
第5個屬性是bfOffBits(4字節),表示DIB數據區在bmp文件中的位置偏移量,這裡等於&H00000076=118,表示數據區從文件開始往後數的118字節開始。
BITMAPFILEHEADER結構體這裡就講完了,大家會發現BITMAPFILEHEADER只占了bmp文件開始的14字節長度,但需要 特別說明的是在vb中定義一個BITMAPFILEHEADER結構體變量,其長度占了16個字節,原因就是第1個屬性本來應該只分配2個字節,但實際被 分配了4個字節,多出來2個字節,所以如果想保存一張bmp圖片,寫入BITMAPFILEHEADER結構體時一定要注意這一點。
2、BITMAPINFO結構體部分。
接下來是BITMAPINFO結構體部分.
BITMAPINFO段由兩部分組成:BITMAPINFOHEADER結構體和RGBQUAD結構體。
其中RGBQUAD結構體表示圖片的顏色信息,有些時候可以省略,一般的24位圖片和32位圖片都不帶RGBQUAD結構體,因為DIB數據區直接表 示的RGB值,一般4位圖片和8位圖片才帶有RGBQUAD結構體。(多少位的圖片就是用多少位來表示一個顏色信息,例如4位圖片表示用4個bit來表示 一個顏色信息。)一個bmp文件中有沒有RGBQUAD結構體,可以根據前面BITMAPFILEHEADER結構體的第5個屬性bfOffBits來判 斷,因為BITMAPINFOHEADER結構體長度為40bit,如果BITMAPINFOHEADER結構體結束後還未到DIB數據區的偏移量,就說 明接下來的數據是RGBQUAD結構體部分。這裡講的C:\WINDOWS\Blue Lace 16.bmp是一個4bit圖片,所以它帶有RGBQUAD結構體。
3、BITMAPINFOHEADER部分。
下面進入正題BITMAPINFOHEADER部分。
第1個屬性是biSize(4字節),表示BITMAPINFOHEADER結構體的長度,最常見的長度是40字節,UltraEdit中可以看到緊接著的4個字節等於&H00000028=40字節。
第2個屬性是biWidth(4字節),表示bmp圖片的寬度,這裡等於&H00000030=48像素。
第3個屬性是biHeight(4字節),表示bmp圖片的高度,這裡等於&H00000030=48像素。
第4個屬性是biPlanes(2字節),表示bmp圖片的平面屬,顯然顯示器只有一個平面,所以恆等於1,這裡等於&H0001。
第5個屬性是biBitCount(2字節),表示bmp圖片的顏色位數,即24位圖、32位圖等等。
這裡等於&H0004,表示該圖片為4位圖。
第6個屬性是biCompression(4字節),表示圖片的壓縮屬性,bmp圖片是不壓縮的,等於0,所以這裡為&H00000000。
第7個屬性是biSizeImage(4字節),表示bmp圖片數據區的大小,當上一個熟悉biCompression等於0時,這裡的值可以省略不填,所以這裡等於&H00000000。
第8個屬性是biXPelsPerMeter(4字節),表示圖片X軸每米多少像素,可省略,這裡等於&H00000EC3=3779像素/米。
第9個屬性是biYPelsPerMeter(4字節),表示圖片Y軸每米多少像素,可省略,這裡等於&H00000EC3=3779像素/米。
第10個屬性是biClrUsed(4字節),表示使用了多少個顏色索引表,一般biBitCount屬性小於16才會用到,等於0時表示有2^biBitCount個顏色索引表,所以這裡仍等於&H00000000。
第11個屬性是biClrImportant(4字節),表示有多少個重要的顏色,等於0時表示所有顏色都很重要,所以這裡等於&H00000000。
至此BITMAPINFOHEADER結構體結束。
4、RGBQUAD結構體
由於這個圖片到這裡還未到達DIB數據區的偏移量,所以接下來的部分是RGBQUAD結構體。
RGBQUAD結構體由4個字節型數據組成,所以一 個RGBQUAD結構體只占用4字節空間,從左到右每個字節依次表示(藍色,綠色,紅色,未使用)。舉例的這個圖片我數了數總共有16個RGBQUAD結 構體,由於該圖片是4位圖,2^4正好等於16,所以它把16種顏色全部都枚舉出來了,這些顏色就是一個顏色索引表。顏色索引表編號從0開始,總共16個 顏色,所以編號為0-15。從UltraEdit中可以看到按照順序,這16個
RGBQUAD結構體依次為:
編號:(藍,綠,紅,空)
0號:(00,00,00,00)
1號:(00,00,80,00)
2號:(00,80,00,00)
3號:(00,80,80,00)
4號:(80,00,00,00)
5號:(80,00,80,00)
6號:(80,80,00,00)
7號:(80,80,80,00)
8號:(C0,C0,C0,00)
9號:(00,00,FF,00)
10號:(00,FF,00,00)
11號:(00,FF,FF,00)
12號:(FF,00,00,00)
13號:(FF,00,FF,00)
14號:(FF,FF,00,00)
15號:(FF,FF,FF,00)
為了更直觀的表示這些顏色,可以見後面的圖片。
到這裡,正好滿足DIB數據區的偏移量,所以後面的字節就是圖片內容了。這裡需要提醒的是所有的DIB數據掃描行是上下顛倒的,也就是說一幅圖片先繪制底部的像素,再繪制頂部的像素,所以這些DIB數據所表示的像素點就是從圖片的左下角開始,一直表示到圖片的右上角。
由於這裡的圖片是4位圖片,也就是說4bit就表示一個像素,一個字節有8個bit,所以一個字節能表示2個像素。
從UltraEdit中可以看到,DIB數據區第一個字節是&H44,16進制正好是將2進制數每4個一組書寫的,跟4bit圖片正好吻 合,所以&H44表示兩個像素,高位的4表示第一個像素,低位的4表示第二個像素。這裡的4不是表示RGB顏色,而是表示顏色索引號為4,由於索 引號從0開始編號的,所以4表示索引表中第5個顏色,從附圖中可以看到索引號為4的是藍色。這是第一字節,表示的是圖片左下角開始2個像素,如果有PhotoShop打開這個圖片可以看到,左下角2個像素取出來的顏色RGB值正好等於索引表中第5個顏色的RGB值。後面的DIB數據以此類推。
至此一個bmp圖片就全部解析完了,根據這些信息就可以完整的繪制一張bmp圖片來。
============================================
如果你還不明白,還有:
1. BMP文件組成
BMP文件由文件頭、位圖信息頭、顏色信息和圖形數據四部分組成。
2. BMP文件頭
BMP文件頭數據結構含有BMP文件的類型、文件大小和位圖起始位置等信息。
其結構定義如下:
typedef struct tagBITMAPFILEHEADER
{
WORDbfType; // 位圖文件的類型,必須為BM
DWORD bfSize; // 位圖文件的大小,以字節為單位
WORDbfReserved1; // 位圖文件保留字,必須為0
WORDbfReserved2; // 位圖文件保留字,必須為0
DWORD bfOffBits; // 位圖數據的起始位置,以相對於位圖文件頭的偏移量表示,以字節為單位
} BITMAPFILEHEADER;
3. 位圖信息頭
BMP位圖信息頭數據用於說明位圖的尺寸等信息。
typedef struct tagBITMAPINFOHEADER{
DWORD biSize; // 本結構所占用字節數
LONGbiWidth; // 位圖的寬度,以像素為單位
LONGbiHeight; // 位圖的高度,以像素為單位
WORD biPlanes; // 目標設備的級別,必須為1
WORD biBitCount // 每個像素所需的位數,必須是1(雙色),4(16色),8(256色)或24(真彩色)之一
DWORD biCompression; // 位圖壓縮類型,必須是0(不壓縮),1(BI_RLE8壓縮類型)或2(BI_RLE4壓縮類型)之一
DWORD biSizeImage; // 位圖的大小,以字節為單位
LONGbiXPelsPerMeter; // 位圖水平分辨率,每米像素數
LONGbiYPelsPerMeter; // 位圖垂直分辨率,每米像素數
DWORD biClrUsed; // 位圖實際使用的顏色表中的顏色數
DWORD biClrImportant; // 位圖顯示過程中重要的顏色數
} BITMAPINFOHEADER;
4. 顏色表
顏色表用於說明位圖中的顏色,它有若干個表項,每一個表項是一個RGBQUAD類型的結構,定義一種顏色。RGBQUAD結構的定義如下:
typedef struct tagRGBQUAD {
BYTErgbBlue; // 藍色的亮度(值范圍為0-255)
BYTErgbGreen; // 綠色的亮度(值范圍為0-255)
BYTErgbRed; // 紅色的亮度(值范圍為0-255)
BYTErgbReserved; // 保留,必須為0
} RGBQUAD;
顏色表中RGBQUAD結構數據的個數有biBitCount來確定:
當biBitCount=1,4,8時,分別有2,16,256個表項;
當biBitCount=24時,沒有顏色表項。
位圖信息頭和顏色表組成位圖信息,
BITMAPINFO結構定義如下:
typedef struct tagBITMAPINFO {
BITMAPINFOHEADER bmiHeader; // 位圖信息頭
RGBQUAD bmiColors[1]; // 顏色表
} BITMAPINFO;
5. 位圖數據
位圖數據記錄了位圖的每一個像素值,記錄順序是在掃描行內是從左到右,掃描行之間是從下到上。位圖的一個像素值所占的字節數:
當biBitCount=1時,8個像素占1個字節;
當biBitCount=4時,2個像素占1個字節;
當biBitCount=8時,1個像素占1個字節;
當biBitCount=24時,1個像素占3個字節;
Windows規定一個掃描行所占的字節數必須是4的倍數(即以long為單位),不足的以0填充,
一個掃描行所占的字節數計算方法:
DataSizePerLine= (biWidth* biBitCount+31)/8; // 一個掃描行所占的字節數
DataSizePerLine= DataSizePerLine/4*4; // 字節數必須是4的倍數
位圖數據的大小(不壓縮情況下):
DataSize= DataSizePerLine* biHeight;