最近为了测试C6678需要读取图像,考虑到效率问题,我用C语言写了一个读取Bitmap的子程序.
这个程序我之前写过,然后这里简单修改了一下以方便C6678在CCS上测试。
本文先简要介绍这段程序,具体测试请关注后续博文。
这段程序是通用的,可以在别处引用,具体代码如下。
头文件Bitmapper.h
/*
* Bitmapper.h
*
* Created on: 2014年12月18日
* Author: fengyhack
*/
#ifndef BITMAPPER_H
#define BITMAPPER_H
#include <stdio.h>
#include <stdlib.h>
//#include <memory.h>
#pragma warning(disable:4996)
typedef unsigned char BYTE;
typedef unsigned short WORD;
typedef unsigned int DWORD;
typedef long LONG;
typedef int BOOL;
#define TRUE 1
#define FALSE 0
#ifndef NULL
#define NULL 0
#endif
#define BM_WORD 0x4D42
typedef struct tagBitmapHeader
{
//WORD bfType; //Constant = 0x424D or “BM”
DWORD bfSize;
WORD bfReserved1;
WORD bfReserved2;
DWORD bfOffBits;
DWORD biSize;
LONG biWidth;
LONG biHeight;
WORD biPlanes; //Constant = 1
WORD biBitCount;
DWORD biCompression;
DWORD biSizeImage;
LONG biXPelsPerMeter;
LONG biYPelsPerMeter;
DWORD biClrUsed;
DWORD biClrImportant;
}BITMAPHEADER;
typedef struct tagRGBQUAD
{
BYTE rgbBlue;
BYTE rgbGreen;
BYTE rgbRed;
BYTE rgbReserved;
}RGBQUAD;
typedef struct tagC3RGB
{
BYTE red;
BYTE green;
BYTE blue;
}C3RGB;
typedef struct tagBufferDims
{
int width;
int height;
int depth;
}BUFFERDIMS;
extern void printType(const int t);
extern void ShowBitmapInfo(BITMAPHEADER* pBitmapHeader);
extern BOOL OpenBitmapFile(const char* szFileName, FILE** ppFile);
extern BOOL ReadHeaderInfo(FILE* pFile, BITMAPHEADER* pBitmapHeader, BOOL dumpInfo);
extern BOOL ReadImageData(FILE* pFile, BITMAPHEADER* pBitmapHeader, BYTE** ppBuffer, BUFFERDIMS* pBufferDims);
extern BOOL ReadBitmapFile(const char* szFileName, BYTE** ppBuffer, BUFFERDIMS* pBufferDims, BOOL dumpInfo);
#endif
实现文件Bitmapper.c
/*
* Bitmapper.c
*
* Created on: 2014年12月18日
* Author: fengyhack
*/
#include "Bitmapper.h"
void printType(const int t)
{
printf("\tCompress type: ");
switch (t)
{
case 0:
printf("UNCOMPRESSED");
break;
case 1:
printf("BI_RLE8");
break;
case 2:
printf("BI_RLE4");
break;
default:
break;
}
printf("\n");
}
void ShowBitmapInfo(BITMAPHEADER* pBitmapHeader)
{
printf("\n------------------- INFORMATION -------------------\n");
printf("\tFile volume : %d Bytes\n", pBitmapHeader->bfSize);
printf("\tContent volume : %d Bytes\n", pBitmapHeader->biSizeImage);
printf("\tImage size : %d*%d (pixel)\n", pBitmapHeader->biWidth, pBitmapHeader->biHeight);
printf("\tNumber of color used: %d\n", pBitmapHeader->biClrUsed);
printf("\tNumber of bit per pixel: %d\n", pBitmapHeader->biBitCount);
//printf("Compress type: %d\n", pBitmapHeader->biCompression);
printType(pBitmapHeader->biCompression);
printf("----------------------------------------------------\n");
}
BOOL OpenBitmapFile(const char* szFileName, FILE** ppFile)
{
*ppFile = fopen(szFileName, "rb");
if (*ppFile == NULL)
{
printf(">>>ERROR:\nFailed to open file %s\n", szFileName);
return FALSE;
}
return TRUE;
}
BOOL ReadHeaderInfo(FILE* pFile, BITMAPHEADER* pBitmapHeader, BOOL dumpInfo)
{
WORD bfType;
fread(&bfType, sizeof(WORD), 1, pFile);
//if ( BM_WORD != (pBitmapHeader->bfType) )
if (BM_WORD!=bfType)
{
printf(">>>INVALID:\nThis is not a valid bitmap.\n");
return FALSE;
}
fread(pBitmapHeader, sizeof(BITMAPHEADER), 1, pFile);
if (dumpInfo)
{
ShowBitmapInfo(pBitmapHeader);
}
return TRUE;
}
BOOL ReadImageData(FILE* pFile, BITMAPHEADER* pBitmapHeader, BYTE** ppBuffer, BUFFERDIMS* pBufferDims)
{
pBufferDims->width = pBitmapHeader->biWidth;
pBufferDims->height = pBitmapHeader->biHeight;
pBufferDims->depth = (pBitmapHeader->biBitCount) >> 3;
if (pBufferDims->depth == 1)
{
printf("This is a grayscale image\n");
}
else if (pBufferDims->depth == 3)
{
printf("This is a RGB colored image\n");
}
else
{
printf(">>>INVALID:\nThis is not a 8/24 bit image.\n");
return FALSE;
}
if (pBufferDims->depth == 1)
{
RGBQUAD palette[256];
int i;
for (/*int */i = 0; i<pBitmapHeader->biClrUsed; ++i)
{
fread(&palette[i], sizeof(RGBQUAD), 1, pFile);
}
}
const int nLineBytes = ((pBitmapHeader->biWidth)* (pBitmapHeader->biBitCount) + 31) / 8;
const size_t bufferSize = nLineBytes*(pBitmapHeader->biHeight);
printf("Trying to allocate %d bytes memory...",bufferSize);
*ppBuffer = (BYTE*)malloc(bufferSize);
if (*ppBuffer == NULL)
{
printf("Failed.\n");
printf(">>>ERROR:\nFailed to allocate memory.\n");
return FALSE;
}
printf("OK.\nPrepare to load image data...\n");
fread(*ppBuffer, bufferSize, 1, pFile);
printf("Image data loaded.\n");
return TRUE;
}
BOOL ReadBitmapFile(const char* szFileName, BYTE** ppBuffer, BUFFERDIMS* pBufferDims, BOOL dumpInfo)
{
FILE* pFile = NULL;
BITMAPHEADER bmh;
//memset(&bmh, 0, sizeof(BITMAPHEADER));
BOOL status = FALSE;
if(OpenBitmapFile(szFileName, &pFile))
{
if(ReadHeaderInfo(pFile, &bmh,dumpInfo))
{
status=ReadImageData(pFile, &bmh, ppBuffer,pBufferDims);
if (status)
{
fclose(pFile);
}
}
}
return status;
}
其中
typedef struct tagBitmapHeader
{
//WORD bfType; //Constant = 0x424D or “BM”
DWORD bfSize;
WORD bfReserved1;
WORD bfReserved2;
DWORD bfOffBits;
DWORD biSize;
LONG biWidth;
LONG biHeight;
WORD biPlanes; //Constant = 1
WORD biBitCount;
DWORD biCompression;
DWORD biSizeImage;
LONG biXPelsPerMeter;
LONG biYPelsPerMeter;
DWORD biClrUsed;
DWORD biClrImportant;
}BITMAPHEADER;
定义的Bitmap Header结构是Bitmap File Header 和Bitmap Info Header的综合
但是其中第一个WORD被注释掉了,至于为何,请继续阅读。
如果我们查看一幅位图(Bitmap)的ASCII编码内容,可能会是如下这个样子
因为我们将其二进制以ASCII方式查看,所以有一些是不可打印字符。
但是这些数据都有一个共同特点,就是开头两个字节(也就是一个WORD)都是“BM”
因此,为了判断一个文件是否Bitmap,首先读取开头的一个WORD,如果不是“BM”那它一定不是Bitmap
开头2个字节是公共的“BM”标识
接下来的12个字节依次如下
DWORD bfSize; // 文件大小(除了位图数据,还有文件属性等信息)
WORD bfReserved1; // 保留
WORD bfReserved2; // 保留
DWORD bfOffBits; // 偏移量
其中,文件大小就是整个文件占用的字节数;
偏移量是指从文件开头开始,往后移动相应字节数就到达图像像素数据内容存放的首地址
再接下来的40个字节依次如下
DWORD biSize; // 位图存储占用的字节数
LONG biWidth; // 宽度(像素)
LONG biHeight; // 高度(像素)
WORD biPlanes; //平面数,常量=1
WORD biBitCount; // 每个像素用多少个bit来表示,常见的有8bit,24bit等
DWORD biCompression; // 压缩类型,0未压缩,1为RLE8,2为RLE4
DWORD biSizeImage; // 这个不一定等于宽*高*单个像素占用,具体原因见后文
LONG biXPelsPerMeter; // 水平方向分辨率 (每米多少个像素)
LONG biYPelsPerMeter; // 垂直方向分辨率 (每米多少个像素)
DWORD biClrUsed; // 使用的颜色数 (仅灰度图使用,RGB设置为0)
DWORD biClrImportant; // 重要的颜色数
位图数据存储时遵从“4字节对齐”原则:
图像数据按行存储时,一行从头到尾存放,如果这一行恰好凑成4字节的整数倍就正好
否则在末尾填充相应字节,例如一行存储占用401字节,那么就需要填充3个字节,如果是399字节,就需要填充1个字节,等等
这个原则是为了“存取迅速”而设定的
根据这个原则,如果一幅图像数据当中,每一行占用字节数不是4的整数倍,那么在存储时会有填充,
因此上述的biSizeImage可能比“宽*高*单个像素占用”要大
如果图像像素数据中每一行恰好是4的整数倍,那么biSizeImage就和“宽*高*单个像素占用”相等
在读取了BITMAPHEADER之后,就相当于获取了关于这个位图的基本信息,然后根据这些信息,
在内存中开辟存储空间,读取数据并惊醒处理。
实际测试发现
如果将BITMAPHEADER改为下述的
typedef struct tagBitmapHeader
{
WORD bfType;
DWORD bfSize;
WORD bfReserved1;
WORD bfReserved2;
DWORD bfOffBits;
DWORD biSize;
LONG biWidth;
LONG biHeight;
WORD biPlanes; //Constant = 1
WORD biBitCount;
DWORD biCompression;
DWORD biSizeImage;
LONG biXPelsPerMeter;
LONG biYPelsPerMeter;
DWORD biClrUsed;
DWORD biClrImportant;
}BITMAPHEADER;
然后如此判断
//BITMAPHEADER* pBitmapHeader = ...
fread(pBitmapHeader, sizeof(BITMAPHEADER), 1, pFile);
if (BM_WORD != (pBitmapHeader->bfType))
{
//...
}
因此最后改成本文开头附上的代码,将第一个DWORD bfType分离出来
typedef struct tagBitmapHeader
{
DWORD bfSize;
WORD bfReserved1;
WORD bfReserved2;
DWORD bfOffBits;
DWORD biSize;
LONG biWidth;
LONG biHeight;
WORD biPlanes; //Constant = 1
WORD biBitCount;
DWORD biCompression;
DWORD biSizeImage;
LONG biXPelsPerMeter;
LONG biYPelsPerMeter;
DWORD biClrUsed;
DWORD biClrImportant;
}BITMAPHEADER;
分离出来的bfType,执行如下判断(这是第一步操作)
WORD bfType;
fread(&bfType, sizeof(WORD), 1, pFile);
if (BM_WORD!=bfType)
{
printf(">>>INVALID:\nThis is not a valid bitmap.\n");
return FALSE;
}
然后再读取BITMAPHEADER数据,这种方式实测可行最后附上一张测试的截图
本文相关源码(包含在测试用例中)可在GitHUb上找到
https://github.com/fengyhack/CodeSnippet/tree/Bitmapper
本文原创,博文原始地址
http://blog.csdn.net/fengyhack/article/details/42103519