BMP(全称Bitmap)是标准图像文件格式,可以分成两类:设备相关位图(DDB)和设备无关位图(DIB),使用非常广。它采用位映射存储格式,除了图象深度可选以外,不采用其他任何压缩。BMP文件的图像深度可选lbit、4bit、8bit及24bit。
NETC下面详细解说BMP图象格式,BMP解码及BMP在LCD显示程序!
一.BMP文件格式
1.位图文件头:14字节
2.位图信息头:40字节
3.彩色表(调色板):4N字节
4.位图数据:根据文件大小
二. 位图文件结构体
1.头包含文件类型、文件大小、存放位置等信息。结构定义如下:
typedef struct tagBITMAPFILEHEADER { UNIT bfType; // 位图文件的类型,必须为BM(0-1字节) DWORD bfSize; // 位图文件的大小,以字节为单位(2-5字节) UINT bfReserved1; // 位图文件保留字,必须为0(6-7字节) UINT bfReserved2; // 位图文件保留字,必须为0(8-9字节) DWORD bfOffBits; // 说明实际图形数据的偏移量(10-13字节) }BITMAPFILEHEADER;
2.位图信息头包含位图的大小、压缩类型、和颜色格式,结构定义如下:
typedef struct tagBITMAPINFOHEADER { DWORD biSize; //说明BITMAPINFOHEADER结构所需字节数 LONG biWidth; //说明图像宽度 LONG biHeight; //说明图像高度 WORD biPlanes; //为目标设备说明位面数,其值设为1 WORD biBitCount; //每个像素的位数,单色位图为1,256色为8,24bit为24。 DWORD biCompression; //压缩说明,BI_RGB:无压缩,BI_RLE8:8位RLE压缩,BI_RLE4:4位RLE压缩 DWORD biSizeImage; //说明图像大小,如无压缩,可设为0 LONG biXPelsPerMerer; //水平分辨率 LONG biYPelsPerMerer; //垂直分辨率 DWORD biClrUsed; //位图使用的颜色数 DWORD biClrImportant; //重要颜色数目 }BITMAPINFOHEADER;
3.彩色表包含的元素与位图所具有的颜色数目相同,像素颜色用结构RGBQUAD来表示:
typedef struct tagRGBQUAD
{
BYTE rgbBlue; // 指定蓝色强度 BYTE rgbGreen; //指定绿色强度
BYTE rgbRed; //指定红色强度
BYTE rgbReserved; //保留,设为0
}RGBQUAD;
三.位图数据
位图数据紧跟在彩色表后的是图像数据阵列,图像每一扫描行有连续的字节组成 扫描行由底向上存储,从左向右,阵列中第一字节为左下角像素,最后一字节为右上角像素.
四.BMP位图解码和LCD显示程序
BMP要在LCD上显示最首先要对BMP进行解码,LCD显示需要的是16位的BMP(RGB565 ) R 11111 G 111111 B 11111.也就是提取出BMP的位图数据,可用现成的工具,也可编写解码程序解码。
1.程序解码BMP显示
/******************** (C) COPYRIGHT 2011 NETCreate **************************** * 文件名称 : bmpdisplay.c * 程序作者 : NETCreate (陈建长) - netc.blog.chinaunix.net * 程序版本 : V1.1.0 * 编制日期 : 2011/8/20 * 功能描述 : BMP解码程序. ******************************************************************************* * CPU : STM32F103ZET6 * 时钟: 72MHz * 声明:算法均有NETC编写,转载请注明出处. ******************************************************************************/
/* Includes ------------------------------------------------------------------*/ #include <stdio.h> #include <stdlib.h> #include "stm32100e_eval_lcd.h" /* SDIO_SD include */ #include "stm32_eval_sdio_sd.h" #include "ff.h" #include "diskio.h" #define NULL 0 #define BYTE_PER_PIXEL 2 #define MAX_X 320 #define MAX_Y 240
#pragma pack(1) /* 设置结构体的边界对齐为1个字节,也就是所有数据在内存中是连续存储的 */ /* 头包含文件类型、文件大小、存放位置等信息 */ typedef struct { short bfType; /* 位图文件的类型,必须为BM(0-1字节) */ long bfSize; /* 位图文件的大小,以字节为单位(2-5字节) */ short bfReserved1; /* 位图文件保留字,必须为0(6-7字节) */ short bfReserved2; /* 位图文件保留字,必须为0(8-9字节) */ long bfoffBits; /* 说明实际图形数据的偏移量(10-13字节) */ } BITMAPFILEHEADER; #pragma pack()
/* Private functions ---------------------------------------------------------*/ /* 私有函数------------------------------------------------------------------*/ /******************************************************************************* * 函数名称: displaybmp. * 功能描述: 从FATFS文件系统中读一张BMP并在指定的坐标显示. * 输入参数: x,y, *path (path 为路径 如“test.bmp”). * 输出参数: None * 返回参数: None *******************************************************************************/ void displaybmp(unsigned int x,unsigned int y, const char *path) { /* FATFS */ FRESULT res; /* FatFs 函数公共结果代码 */ FIL fbmp; /* 文件对象 */ UINT br; /* 文件读写字节字节计数 */
long height; long width; unsigned int i,j; BITMAPFILEHEADER bfile;
unsigned char *pDIBData = NULL; unsigned char r = 0, g = 0, b = 0; unsigned short R = 0, G = 0, B = 0; unsigned short color = 0;
/* OPEN FILE */ res = f_open(&fbmp,path, FA_OPEN_EXISTING | FA_READ); if (res == FR_OK ) printf(" Open file OK! \n\r"); else printf(" Open file fail! \n\r");
/* READ FILE 读取文件头信息 */ res = f_read(&fbmp, &bfile, sizeof(bfile), &br); printf(" bfsize=%d, bfoffbits=%d \n\r",bfile.bfSize, bfile.bfoffBits); /* 获取文件的宽度 高度 f_lseek 从18个字节开始读*/ f_lseek(&fbmp, 18); f_read(&fbmp, (void *)&width, sizeof(long),&br); f_read(&fbmp, (void *)&height, sizeof(long),&br); printf(" width =%d \n\r",width); printf(" height =%d \n\r",height); for (i = 0; i < height; i++) for (j = 0; j < width; j++) { /* 分配图像数据3个字节存储空间,实际的图像数据大小:文件大小 - 图像数据与文件头的偏移量 */ /* 如果RAM够大 可以分配整个文件 pDIBData = (unsigned char *)malloc(bfile.bfSize - bfile.bfoffBits); */ pDIBData = (unsigned char *) malloc(3); if(pDIBData == NULL) { printf("mem allocate failed\n"); break; }
/* 读取图像数据3个字节至分配的存储空间 */ /* **lseek( fp_bmp, bfile.bfoffBits, SEEK_SET ); 如果RAM够大一次读出 **read( fp_bmp, pDIBData, bfile.bfSize - bfile.bfoffBits); */ f_lseek(&fbmp, bfile.bfoffBits +=3); f_read(&fbmp, pDIBData,3,&br);
/* 显示图像,这里需要注意如何将RGB24转换为RGB565 */ /* 注意从bmp文件中读出的图像数据连续的三个字节BGR */ /* 红色r占5为屏蔽低三位 绿色g占6为屏蔽低两位 蓝色b占5为屏蔽低三位 */ /* 先读出的三个字节对应的是图像中的左下角的那个像素,最后三个字节对应图像 右上角*/ b = *pDIBData ; g = *(pDIBData + 1) ; r = *(pDIBData + 2); r = r & (~(0x07)); g = g & (~(0x03)); b = b & (~(0x7)); R = (unsigned short)r << 8; G = (unsigned short)g << 3; B = (unsigned short)b >> 3; color = R | G | B; /* 画图 左下开始 */ if ( ( (0+j) < MAX_X) && ( (0+i) < MAX_Y) ) DrawPoint(x+j,y+height-i,color); free(pDIBData); } f_close(&fbmp); }
2.用现成的软件工具解码得出位图数据
a.直接取一张16位BMP位图时,biBitCount=16,1个像素占2个字节,从R -> G -> B bit(15-0)左到右,上到下取数据 ,把G往R和B分三位。如:
R 0XF8,0X00 11111000 00000000
G 0X07,0XE0 00000111 11100000
B 0X00,0X1F 00000000 00011111 程序实现:
/******************************************************************************* * 函数名称: Paint_Bmp16. * 功能描述: 在LCD屏幕上指定坐标点画一个指定大小的BMP图片 NETC-陈建长 程序. * 输入参数: unsigned int x,unsigned int y,显示起始位置 unsigned int width,unsigned int high,BMP大小 unsigned char bmp[] BMP数据数组首地址 如 gImage_LCDTEST. * 输出参数: None * 返回参数: None *******************************************************************************/ void paint_Bmp16(unsigned int x,unsigned int y,unsigned int width,unsigned int high, unsigned char bmp[]) { unsigned short color; /* 像素颜色 */ unsigned int p = 0,i,j; for(i=0;i<high;i++) /* 取一张BMP数据从R -> G -> B bit(15-0),左到右,上到下左起为最高位存数据 */ { /* 为两个8位的数据 存一个像素 把G往R和B分三位 */ for(j=0;j<width;j++) /* R 0XF8,0X00 G 0X07,0XE0 B 0X00,0X1F */ { /* RGB565 分为2个数据保存 第一个X 0x100 | 第二个后还原一点像素 */ color = ( (bmp[p] * 0x100) | (bmp[p+1]) ); if ( ( (x+j) < SCR_XSIZE_TFT) && ( (y+i) < SCR_YSIZE_TFT) ) PutPixel(x+j,y+i,color); p = p + 2; } } }
b.24位BMP RGB24转RGB565显示程序:
/******************************************************************************* * 函数名称: Paint_Bmp24. * 功能描述: 在LCD屏幕上指定坐标点画一个指定大小的BMP图片,RGB24转换为RGB565 按BMP位图数据的存放格式, 读出的三个字节对应的是图像中的左下角的像素, 最后三个字节对应图像.右上角的像素,连续的三个字节中第一个字节是蓝色分量, 第二是绿色分量,第三个字节为红色分量,只需把BMP数据指针给函数即可转换. NETC-陈建长 程序. * 输入参数: unsigned int x,unsigned int y,显示起始位置 unsigned int width,unsigned int high,BMP大小 unsigned char *pDIBData BMP数据数组首地址 如 gImage_LCDTEST. * 输出参数: None * 返回参数: None *******************************************************************************/ void paint_Bmp24(unsigned int x,unsigned int y,unsigned int width,unsigned int high,unsigned char *pDIBData) { unsigned short color; /* 像素颜色 */ unsigned int i,j; unsigned short r,g,b,R,G,B; for (i = 0; i < high; i++) for (j = 0; j < width; j++) { /* 从bmp文件中读出的图像数据连续的三个字节中第一个字节为蓝色分量 */ /* 第二个字节为绿色分量 第三个字节为红色分量 */ b = *pDIBData ; g = *(pDIBData + 1) ; r = *(pDIBData + 2);
/* RGB24转换为RGB565 */ /* 红色r占5为屏蔽低三位 绿色g占6为屏蔽低两位 蓝色b占5为屏蔽低三位 */ r = r & (~(0x07)); g = g & (~(0x03)); b = b & (~(0x7)); R = (unsigned short)r << 8; G = (unsigned short)g << 3; B = (unsigned short)b >> 3; color = R | G | B; /* ** 计算偏移量,最先读出的三个字节对应的是图像中的左下角的那个像素 ** 最后三个字节对应图像 右上 角 的那个像素 */ if ( ( (0+j) < SCR_XSIZE_TFT) && ( (0+i) < SCR_YSIZE_TFT) ) PutPixel(x+j,y+high-i,color); pDIBData += 3; }
}
|