主要在之前的解碼的基礎上面添加了圖片縮放功能。
大家可以看看BMP解碼過程或者思路。
資料網(wǎng)上非常多,在此就不在說明,直接上代碼。
BMPdecode.c
/************************************************************************************************************* ?*?文件名: bmpdecode.c ?*?功能: BMP圖片軟件解碼 ?*?作者: cp1300@139.com ?*?創(chuàng)建時間: 2012年12月7日20:30 ?*?最后修改時間:2012年12月9日 ?*?詳細: 只支持非壓縮的BMP,16bit,24bit,32bit ?*? 圖片文件最大由BMP_MAX_BUFF決定 ?*? 因為使用了FATFS,以及比較多的臨時變量,內(nèi)聯(lián)函數(shù)可能需要比較大的堆棧 ?*? 因為添加了圖片縮放功能,因此程序比以前的效率稍微低下了一點,主要是因為畫點的時候需要判斷了 *************************************************************************************************************/ #include?"tft_lcd.h" #include?"system.h" #include?"bmpdecode.h" #include?"ff.h" //圖片緩沖區(qū),目前定義為10MB,也就意味著最大只能打開10MB的位圖文件 #define?BMP_MAX_BUFF??10*1024*1024 //定義緩沖區(qū)最大大小 static?u8?BmpImageBuff[BMP_MAX_BUFF]; //圖片緩沖區(qū) //顯示窗口的最大值,一般定義為顯示器大小 #define?LCD_MAX_WIDTH 800 //最大寬度 #define?LCD_MAX_HEIGHT 480 //最大高度 //圖像數(shù)據(jù)壓縮類型,目前只支持沒有壓縮的位圖 #define?BI_RGB???????0 //沒有壓縮 #define?BI_RLE8??????1 //每個象素8比特的RLE壓縮編碼,壓縮格式由2字節(jié)組成(重復象素計數(shù)和顏色索引) #define?BI_RLE4??????2 //每個象素4比特的RLE壓縮編碼,壓縮格式由2字節(jié)組成 #define?BI_BITFIELDS?3 //每個象素的比特由指定的掩碼決定 //16,24,32位BMP文件頭部信息結構 typedef??struct { u16?Invalid; //無效的填充字節(jié),用于讓數(shù)據(jù)對齊 ? u16?bfType?;? //文件標志.只對'BM',用來識別BMP位圖類型 u32?bfSize?;? //文件大小,占四個字節(jié) u32?bfReserved1?; //保留 u32?bfOffBits?;? //從文件開始到位圖數(shù)據(jù)(bitmap?data)開始之間的的偏移 u32?bmfHeaderSize; //圖像描述信息塊的大小,常為28H u32?biWidth?;? //說明圖象的寬度,以象素為單位 u32?biHeight?;? //說明圖象的高度,以象素為單位 u16?biPlanes?;? //為目標設備說明位面數(shù),其值將總是被設為1 u16?biBitCount?;? //說明比特數(shù)/象素,其值為1、4、8、16、24、或32 u32?biCompression?;?//說明圖象數(shù)據(jù)壓縮的類型。其值可以是下述值之一: //BI_RGB:沒有壓縮; //BI_RLE8:每個象素8比特的RLE壓縮編碼,壓縮格式由2字節(jié)組成(重復象素計數(shù)和顏色索引); //BI_RLE4:每個象素4比特的RLE壓縮編碼,壓縮格式由2字節(jié)組成 //BI_BITFIELDS:每個象素的比特由指定的掩碼決定。 u32?biSizeImage?; //說明圖象的大小,以字節(jié)為單位。當用BI_RGB格式時,可設置為0 u32?biXPelsPerMeter?;//說明水平分辨率,用象素/米表示 u32?biYPelsPerMeter?;//說明垂直分辨率,用象素/米表示 u32?biClrImportant?;?//說明對圖象顯示有重要影響的顏色索引的數(shù)目,如果是0,表示都重要。 }BMPFILEHEADER; //BMP圖片相關的信息結構 struct?BMP_IMAGE { u16?bfOffBits?;? //從文件開始到位圖數(shù)據(jù)(bitmap?data)開始之間的的偏移 u16?biWidth?;? //說明圖象的寬度,以象素為單位 u16?biHeight?;? //說明圖象的高度,以象素為單位 u16 biBitCount?;? //說明比特數(shù)/象素,其值為1、4、8、16、24、或32 u32?biSizeImage; //位圖數(shù)據(jù)的大小 u32?biSizeFile; //位圖文件大小 }BmpFile; //圖像信息 struct?BMPPIC_POS { ? u32?ImgWidth; //圖像的實際寬度和高度 u32?ImgHeight; u32?Div_Fac; //圖像縮放系數(shù)(擴大了10000倍) u32?S_Height; //設定的顯示高度和寬度 u32?S_Width; u32 S_XOFFSET; //X,Y起始偏移量 u32?S_YOFFSET; u32?staticx; //當前顯示的X,Y坐標 u32?staticy; }BMPPICINFO; //內(nèi)部函數(shù)聲明 static?BMP_ERROR?OpenBmpFile(const?char?*FileName,u8?*buff,u32?FileMaxSize); //打開BMP圖像,并將數(shù)據(jù)讀取到內(nèi)存 __inline?u8?ReadByteData(u32?Offset); //讀取一字節(jié)指定偏移的圖片數(shù)據(jù) static?BMP_ERROR?DecodingHead(u8?*FileBuff); //解碼BMP圖片頭部 __inline?void?BmpImageDrow(u16?x,u16?y,u16?data); //BMP圖像畫點函數(shù) static?BMP_ERROR?BmpDecode(u16?x1,u16?y1,u16?x2,u16?y2,u8?*BmpBuff); //BMP圖像解碼核心 static?void?ImageDrow_Init(void); //BMP畫點函數(shù)初始化 /************************************************************************************************************************* *函數(shù)???????? : static?BMP_ERROR?OpenBmpFile(const?char?*FileName,u8?*buff,u32?FileMaxSize) *功能???????? : 打開BMP圖像,并將數(shù)據(jù)讀取到內(nèi)存 *參數(shù)???????? : FileName:文件名,路徑指針;buff:讀取緩沖區(qū);FileMaxSize:文件最大限制 *返回???????? : BMP_ERROR *依賴????? :? FATFS文件系統(tǒng)支持 *作者???????? : cp1300@139.com *時間????? : 20121207 *最后修改時間 : 20121207 *說明????? : 調(diào)用FATFS打開BMP圖片文件 *************************************************************************************************************************/ static?BMP_ERROR?OpenBmpFile(const?char?*FileName,u8?*buff,u32?FileMaxSize) { FIL?file; UINT?cnt; int?status; BMP_ERROR?error?=?BMP_OK; status?=?f_open(&file,FileName,FA_READ); //只讀方式打開文件 if(status?!=?FR_OK) //打開文件錯誤 { uart_printf("open?"%s"?error!rn",FileName); status?=?BMP_OPEN_ERROR; } else { //獲取文件大小 uart_printf("file?size?:?%dBrn",file.fsize); //輸出文件大小 if(file.fsize?>?FileMaxSize) { uart_printf("file?size?>?%drn",FileMaxSize); status?=?BMP_SIZE_ERROR; } else { status?=?f_read(&file,buff,file.fsize,&cnt); //讀取文件 uart_printf("Read?File?Num=%drn",cnt); if(cnt?==?file.fsize) //判斷文件是否讀取完畢 { uart_printf("read?file?end!rn"); BmpFile.biSizeFile?=?file.fsize; //存儲位圖文件大小 status?=??BMP_OK; } else { uart_printf("read?file?error!rn"); status?=?BMP_READ_ERROR; } } } f_close(&file); return?error; } /************************************************************************************************************************* *函數(shù)???????? : __inline?u8?ReadByteData(u32?Offset) *功能???????? : 讀取一字節(jié)指定偏移的圖片數(shù)據(jù) *參數(shù)???????? : Offset:地址偏移 *返回???????? : 數(shù)據(jù) *依賴????? :? 無 *作者???????? : cp1300@139.com *時間????? : 20121207 *最后修改時間 : 20121207 *說明????? : 方便移植 *************************************************************************************************************************/ __inline?u8?ReadByteData(u32?Offset) { return?BmpImageBuff[Offset]; } /************************************************************************************************************************* *函數(shù)???????? : static?BMP_ERROR?DecodingHead(u8?*FileBuff) *功能???????? : 解碼BMP圖片頭部 *參數(shù)???????? : *FileBuff:bmp圖像數(shù)據(jù)緩沖區(qū)指針 *返回???????? : BMP_ERROR *依賴????? :? static?BMP_ERROR?OpenBmpFile(const?char?*FileName,u8?*buff,u32?FileMaxSize) *作者???????? : cp1300@139.com *時間????? : 20121207 *最后修改時間 : 20121207 *說明????? : 注意BMP的頭部信息并沒有按照32BIT對齊,在前面插了兩個字節(jié)后即可對齊,非對齊訪問會發(fā)生異常 *************************************************************************************************************************/ static?BMP_ERROR?DecodingHead(u8?*FileBuff) { BMP_ERROR?error?=?BMP_OK; u8?buff[52]; u8?cnt; BMPFILEHEADER?*pbmp;//臨時指針,用于獲取BMP文件的頭部信息 u8?*p?=?buff?+?2; //BMP頭部共50字節(jié),前面的兩個字節(jié)用于偏移對齊 for(cnt?=?0;cnt?<?50;cnt?++) { p[cnt]?=?FileBuff[cnt]; //復制(讀?。╊^部到緩沖區(qū) } pbmp?=?(BMPFILEHEADER*)buff; //得到BMP的頭部信息 BmpFile.bfOffBits?=?pbmp->bfOffBits;? //位圖數(shù)據(jù)偏移地址偏移 BmpFile.biBitCount?=?pbmp->biBitCount; //位圖數(shù)據(jù)的顏色深度只支持16bit,24bit,32bit BmpFile.biWidth?=?pbmp->biWidth; //位圖的水平像素數(shù) BmpFile.biHeight?=?pbmp->biHeight; //位圖的垂直像素數(shù) BmpFile.biSizeImage?=?pbmp->biSizeImage;//位圖的數(shù)據(jù)大小,有的圖片這個值為0 /****************************************************/ //調(diào)試 uart_printf("地址偏移:%drn",BmpFile.bfOffBits); uart_printf("顏色深度:%drn",BmpFile.biBitCount); uart_printf("水平分辨率:%drn",BmpFile.biWidth); uart_printf("垂直分辨率:%drn",BmpFile.biHeight); uart_printf("數(shù)據(jù)大小:%drn",BmpFile.biSizeImage); uart_printf("位圖標志:%Xrn",pbmp->bfType); /****************************************************/ if(pbmp->bfType?==?0x4d42) //檢測位圖頭部信息 { if((BmpFile.biBitCount?!=?16)?&&?(BmpFile.biBitCount?!=?24)?&&?(BmpFile.biBitCount?!=?32)) error?=?BMP_TYPE_ERROR; else error?=?BMP_OK; } else error?=?BMP_TYPE_ERROR; return?error; } /************************************************************************************************************************* *函數(shù)???????? : __inline?void?BmpImageDrow(u16?x,u16?y,u16?data) *功能???????? : BMP圖像畫點函數(shù) *參數(shù)???????? : x,y:xy坐標,data:RGB565數(shù)據(jù) *返回???????? : 無 *依賴????? :? 無 *作者???????? : cp1300@139.com *時間????? : 20121207 *最后修改時間 : 20121207 *說明????? : 因為進行縮放的時候,有些點不用進行顯示 *************************************************************************************************************************/ __inline?void?BmpImageDrow(u16?x,u16?y,u16?data) { ?? if(x?!=?BMPPICINFO.staticx?||?y?!=?BMPPICINFO.staticy) { BMPPICINFO.staticx?=?x; BMPPICINFO.staticy?=?y; LCD_DrawPoint(x+BMPPICINFO.S_XOFFSET,y+BMPPICINFO.S_YOFFSET,data); } } /************************************************************************************************************************* *函數(shù)???????? : static?BMP_ERROR?BmpDecode(u16?x1,u16?y1,u16?x2,u16?y2,u8?*BmpBuff) *功能???????? : BMP圖像解碼核心 *參數(shù)???????? : x1,y1:窗口起始坐標;x2,y2:窗口結束坐標;BmpBuff:圖像緩沖區(qū)指針 *返回???????? : BMP_ERROR *依賴????? :? 需要先解碼頭信息 *作者???????? : cp1300@139.com *時間????? : 20121207 *最后修改時間 : 20121209 *說明????? : 解碼圖像 *************************************************************************************************************************/ static?BMP_ERROR?BmpDecode(u16?x1,u16?y1,u16?x2,u16?y2,u8?*BmpBuff) { BMP_ERROR?error?=?BMP_OK; u32?data; //存放24BIT圖片的一個像素顏色值 u32?bmp_cnt; //已經(jīng)讀取的照片數(shù)據(jù)計數(shù) u16?uiTemp; ?? //x軸方向?qū)嶋H存儲的像素數(shù)據(jù)字節(jié)數(shù) u16?xValid; //x軸方向有效的像素數(shù)據(jù)字節(jié)數(shù) u16?xcnt;? //X軸方向像素點數(shù)據(jù)字節(jié)數(shù)計數(shù) u16?x,y; //畫點坐標 u8?temp; if((BmpFile.biWidth?*?BmpFile.biBitCount?/?8)?%?4)//水平像素字節(jié)數(shù)不是4的整數(shù)倍 uiTemp?=?((BmpFile.biWidth?*?BmpFile.biBitCount?/?8)?/?4?+?1)?*?4;//將水平像素字節(jié)數(shù)擴展成4的整數(shù)倍 else uiTemp?=?BmpFile.biWidth?*?BmpFile.biBitCount?/?8; xValid?=?BmpFile.biWidth?*?(BmpFile.biBitCount?/?8); //計算水平有效地數(shù)據(jù)字節(jié)數(shù) y?=?BMPPICINFO.ImgHeight?-?1; //BMP圖片由左下角到右上角刷新,因此起點y坐標要加上圖片的高度 x?=?0; bmp_cnt?=?BmpFile.bfOffBits;//將文件數(shù)據(jù)開始賦值給數(shù)據(jù)計數(shù)器 xcnt?=?0; //X方向像素點計數(shù)器清零 do { switch?(BmpFile.biBitCount)//判斷圖片顏色深度 { case?32:?//32BIT???ARGB8888??//之所以使用一個字節(jié)讀取一方面方便移植到內(nèi)存較小的單片機上面執(zhí)行,另一方面避免內(nèi)存非對齊訪問產(chǎn)生異常。 { data?=?ReadByteData(bmp_cnt++); temp?=?ReadByteData(bmp_cnt++); data?|=?temp?<<?8; temp?=?ReadByteData(bmp_cnt++); data?|=?temp?<<?16; bmp_cnt?++; data?=?RGB565(data); //RGB888?--->?RGB565 }break; case?24:?//24BIT?RGB888 //可能存在4字節(jié)對齊問題 { data?=?ReadByteData(bmp_cnt++); temp?=?ReadByteData(bmp_cnt++); data?|=?temp?<<?8; temp?=?ReadByteData(bmp_cnt++); data?|=?temp?<<?16; xcnt?+=?3; data?=?RGB565(data); //RGB888?--->?RGB565 }break; case?16:?//16BIT??RGB555?//可能存在4字節(jié)對齊問題 { data?=?ReadByteData(bmp_cnt++); temp?=?ReadByteData(bmp_cnt++); data?|=?temp?<<?8; xcnt?+=?2; data?=?((data?&?0xffe0)?<<?1)?|?(data?&?0x001f);//RGB555??--->??RGB565 data?|=?((data?&?BIT6)???BIT5?:?0); };break; default?:?break;//只支持16BIT,24BIT,32BIT圖片的解碼 } switch(BMPPICINFO.Div_Fac) { case?10000 : BmpImageDrow(x,y,data);break; //圖片無需縮放 default : BmpImageDrow(x*BMPPICINFO.Div_Fac/10000,y*BMPPICINFO.Div_Fac/10000,data);break; //圖片需要縮放 } x?++; if(x?==?BMPPICINFO.ImgWidth) //一行解碼完成,換行 { x?=?0; if(y?==?0)?break; y?--; } if(xcnt?==?xValid) //插值,無效數(shù)據(jù),跳過 { bmp_cnt?+=?uiTemp?-?xcnt; xcnt?=?0; } }while(bmp_cnt??<?BmpFile.biSizeFile); //判斷數(shù)據(jù)是否讀取完畢 uart_printf("bmp_cnt?=?0x%X;?BmpFile.biSizeFile?=?0x%Xrn",bmp_cnt,BmpFile.biSizeFile); return?error; } /************************************************************************************************************************* *函數(shù)???????? : static?void?ImageDrow_Init(void) *功能???????? : BMP畫點函數(shù)初始化 *參數(shù)???????? : 無 *返回???????? : 無 *依賴????? :? 無 *作者???????? : cp1300@139.com *時間????? : 20121207 *最后修改時間 : 20121209 *說明????? : 用于計算顯示坐標以及縮放系數(shù) *************************************************************************************************************************/ static?void?ImageDrow_Init(void) { float?temp,temp1; BMPPICINFO.ImgWidth?=?BmpFile.biWidth; //獲取圖像實際寬度 BMPPICINFO.ImgHeight?=?BmpFile.biHeight; //獲取圖像實際高度 temp=(float)BMPPICINFO.S_Width?/?BMPPICINFO.ImgWidth; temp1=(float)BMPPICINFO.S_Height?/?BMPPICINFO.ImgHeight; if(temp?<?temp1)?temp1?=?temp; //取較小的那個邊的縮放系數(shù) if(temp1?>?1)?temp1?=?1; //不能放大,原圖大小顯示 BMPPICINFO.Div_Fac?=?temp1?*?10000; //將縮放系數(shù)擴大10000倍 //計算偏移,將圖片置于顯示區(qū)域的起始位置 BMPPICINFO.S_XOFFSET?+=?(BMPPICINFO.S_Width?-?temp1?*?BMPPICINFO.ImgWidth)?/?2; BMPPICINFO.S_YOFFSET?+=?(BMPPICINFO.S_Height?-?temp1?*?BMPPICINFO.ImgHeight)?/?2; //將當前顯示坐標放到一個不可能的值上面 BMPPICINFO.staticx?=?0xffff; BMPPICINFO.staticy?=?0xffff; } /************************************************************************************************************************* *函數(shù)???????? : BMP_ERROR?ShowBmpImage(u16?x1,u16?y1,u16?x2,u16?y2,const?char?*BmpFile) *功能???????? : 指定位置顯示一張BMP圖片 *參數(shù)???????? : x1,y1:窗口起始坐標;x2,y2:窗口結束坐標;BmpFile:BMP文件路徑以及名稱 *返回???????? : BMP_ERROR *依賴????? :? static?BMP_ERROR?BmpDecode(u16?x1,u16?y1,u16?x2,u16?y2,u8?*BmpBuff) *作者???????? : cp1300@139.com *時間????? : 20121207 *最后修改時間 : 20121207 *說明????? : 直接在LCD上面顯示一張BMP圖片,需要FATFS支持 *? 當結束窗口坐標為0的時候結束坐標自動換成屏幕結束坐標 *************************************************************************************************************************/ BMP_ERROR?ShowBmpImage(u16?x1,u16?y1,u16?x2,u16?y2,const?char?*BmpFile) { BMP_ERROR?error; BMPPICINFO.S_Width?=?(((x2?-?x1?+?1)?>?LCD_MAX_WIDTH)?||?(x2?==?0))???(LCD_MAX_WIDTH-x1):?(x2?-?x1?+?1); //計算設定的顯示寬度和高度 BMPPICINFO.S_Height?=?(((y2?-?y1?+?1)?>?LCD_MAX_HEIGHT)?||?(y2?==?0))???(LCD_MAX_HEIGHT-y1):?(y2?-?y1?+?1); BMPPICINFO.S_XOFFSET?=?x1; //初始化偏移為起始坐標 BMPPICINFO.S_YOFFSET?=?y1; error?=?OpenBmpFile(BmpFile,BmpImageBuff,BMP_MAX_BUFF); if(error?==?BMP_OK) { error?=?DecodingHead(BmpImageBuff); if(error?==?BMP_OK) { ImageDrow_Init(); error?=?BmpDecode(x1,y1,x2,y2,BmpImageBuff); } } return?error; }
bmpdecode.h
/************************************************************************************************************* ?*?文件名: bmpdecode.h ?*?功能: BMP圖片軟件解碼 ?*?作者: cp1300@139.com ?*?創(chuàng)建時間: 2012年12月7日20:30 ?*?最后修改時間:2012年12月9日 ?*?詳細: 只支持非壓縮的BMP,16bit,24bit,32bit ?*? 圖片文件最大由BMP_MAX_BUFF決定 ?*? 因為使用了FATFS,以及比較多的臨時變量,內(nèi)聯(lián)函數(shù)可能需要比較大的堆棧 *************************************************************************************************************/ #ifndef?BMPDECODE_H_ #define?BMPDECODE_H_ //BMP錯誤定義 typedef?enum { BMP_OK = 0, //正常處理完成 BMP_OPEN_ERROR = 1, //圖像打開錯誤 BMP_READ_ERROR = 2, //圖像讀取錯誤 BMP_SIZE_ERROR??=? 3, //圖像大小錯誤 BMP_TYPE_ERROR??=? 4, //不支持的圖像類型,圖像類型錯誤 BMP_OTHER_ERROR = 5 //其它未知錯誤 }?BMP_ERROR; BMP_ERROR?ShowBmpImage(u16?x1,u16?y1,u16?x2,u16?y2,const?char?*BmpFile); //指定位置顯示一張BMP圖片 #endif?/*BMPDECODE_H_*/
main.c
#include?"system.h" #include?"uart.h" #include?"tft_lcd.h" #include?"other.h" #include?"delay.h" #include?"timer.h" #include?"ff.h" #include?"rtc.h" #include?"bmpdecode.h" #include?"jpeg.h" u8?jpeg_buff[10*1024*1024]; u8?buff[10*1024*1024]; FATFS?fs; //LED1閃爍程序,在定時器0中斷服務程序中閃爍,周期400MS void?LED1_flash(void) { LED1_FLASH(); } void?PrintfDirFile(void) { int?result; DIR?DirInf;?? FILINFO?FileInf; int?cnt; result?=?f_opendir(&DirInf,?"/");?/*?如果不帶參數(shù),則從當前目錄開始?*/ if?(result?!=?FR_OK)? { lcd_printf("Root?Directory?is?Open?Error?(%d)n",?result); } /*?讀取當前文件夾下的文件和目錄?*/ lcd_printf("Name?????????????Tyepe?????????Sizen"); for?(cnt?=?0;?;cnt++)? { result?=?f_readdir(&DirInf,&FileInf);? /*?讀取目錄項,索引會自動下移?*/ if?(result?!=?FR_OK?||?FileInf.fname[0]?==?0) { break; } if?(FileInf.fname[0]?==?'.') { continue; } lcd_printf("%s??",?FileInf.fname); if?(FileInf.fattrib?==?AM_DIR) { lcd_printf("Directory?"); }? else? { lcd_printf("File??"); } lcd_printf("%dn",?FileInf.fsize); } } //主函數(shù) int?main(void) { int?result; LCD_Init(); //初始化LCD UART0_Init(DISABLE,115200); //初始化串口,失能中斷接收,波特率115200 LED_Init(); //初始化LED Timer1_Init(400000-1,ENABLE,LED1_flash); //初始化定時器1,周期400ms RTC_Init(ENABLE); //初始化RTC時鐘 JPEG_Init(); //初始化JPEG解碼器 result?=?disk_initialize?(DISK_SDMMC); //初始化磁盤 if(result?==?RES_OK) { lcd_printf("Disk?Init?OK!n"); result?=?f_mount(DISK_SDMMC,&fs); if(result?==?FR_OK) { lcd_printf("Mount?Disk?OK!n"); PrintfDirFile(); //打印根目錄下的文件 ShowBmpImage(0,0,0,0,"bmp24.BMP"); //顯示全屏圖片 ShowBmpImage(200,200,0,0,"bmp24.BMP"); //顯示縮小的圖片 } else { lcd_printf("Mount?Disk?ERROR(%d)!n",result); } } else { lcd_printf("Disk?Init?ERROR(%d)!n",result); } while(1) { LED2_FLASH(); //LED2閃爍 Delay_US(600000); //lcd_printf("%d-%d-%d?%d:%dn",Timer.w_year,Timer.w_month,Timer.w_date,Timer.hour,Timer.min); } }
開發(fā)板上面的效果圖