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
#include
#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 G -> B bit(15-0),左到右,上到下左起为最高位存数据 */
{ /* 为两个8位的数据 存一个像素 把G往R和B分三位 */
for(j=0;j { /* 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;
}
}
阅读(4335) | 评论(0) | 转发(1) |