读数据文件 生成BMP图像
该程序的功能是读取存有位图颜色数据的数据流,提取颜色数据并进行格式转换,再存为位图文件。即将16位颜色数据转换成24位后以位图文件存储。
数据文件是通过固定转换工具由转换而来的(其实是我的同事写的,所以数据文件的格式是按我的要求生成的),转换后的数据格式为16位565形式的数据,高位在后,低位在前。存在“.c”文件中(存成什么文件不重要)。
数据文件的格式:(可以是多个位图的数据)
/* Plane.bmp 300 197 24 */ ……文件名,宽,高,位数
const uint8 Plane_565[] =
{ 0x5f,0xd7,0x3f,0xd7,……16进制颜色数据
}/* Clock.bmp 346 170 24 */
const uint8 Clock_565[] =
{ 0x5f,0xd7,0x3f,0xd7,……16进制颜色数据
}
程序的主要代码,注释的很详细
/*------------------------------------------------------------------------------
DEFINE DEFINITION
------------------------------------------------------------------------------*/
#define SINGLETRANS 0 /* 标识单图转换 */
#define BATCHTRANS 1 /* 标识批量转换 */
#define FILEEND 1 /*------------------------------------------------------------------------------
GLOBAL DEFINITION
------------------------------------------------------------------------------*/
int iBmpWidth; /* 输入的宽度,以像素为单位 */
int iBmpHeight; /* 输入的高度,以像素为单位 */
int iRealWidth; /* 存储字对齐后的宽度,以字节为单位 */
int iBitcounts; /* 用于存储图像位数 */
int iTransformtype; /* 标识转换类型 */
int fileend = 0; CString lpszPathnamein; /* 输入文件路径 */
CString lpszPathnameout; /* 输出文件路径 */
char Filename[_MAX_FNAME]; /* 数据文件中指定的文件名 */
BYTE * DATAarray; /* 存储读入的图像数据 */
FILE * pfin; /*------------------------------------------------------------------------------
Function: OnButtoninputClickDescription: 打开通用对话框,选择输入文件
Input: none
Output:
Return:
------------------------------------------------------------------------------*/
void CFileToBmpbatchDlg::OnButbrowsein()
{
CString spszFilename;
CFileDialog dlg(TRUE,"*.c",NULL,OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT,\
"The C Files (*.c)|*.c|",NULL);
if (dlg.DoModal() != IDOK)
{
//CommDlgExtendedError();
return;
}
lpszPathnamein = dlg.GetPathName();
/* 显示输入路径和文件名 */
/* 为输出路径添加默认路径和文件名 */
lpszPathnameout = lpszPathnamein.Left(lpszPathnamein.GetLength() \
- spszFilename.GetLength());
m_ctr_edit_outpath.SetWindowText(lpszPathnameout);
}/*------------------------------------------------------------------------------
Function: OnButtonoutputClickDescription: 打开浏览对话框,选择输出路径
Input: none
Output:
Return:
------------------------------------------------------------------------------*/
void CFileToBmpbatchDlg::OnButbrowseout()
{
BROWSEINFO bi; /* BROWSEINFO结构体 */ TCHAR Buffer[MAX_PATH] = "";
TCHAR FullPath[MAX_PATH] = ""; bi.hwndOwner = m_hWnd; /* m_hWnd程序主窗口 */
bi.pidlRoot = NULL;
bi.pszDisplayName = Buffer; /* 返回选择的目录名的缓冲区 */
/* 只返回目录 */
bi.ulFlags = BIF_RETURNONLYFSDIRS | BIF_EDITBOX | BIF_BROWSEFORCOMPUTER;
bi.lpfn = NULL;
bi.lParam = 0;
bi.iImage = 0; ITEMIDLIST *pidl = ::SHBrowseForFolder(&bi); /* 显示弹出窗口 */
/* 在ITEMIDLIST中得到目录名的整个路径 */
if (::SHGetPathFromIDList(pidl,FullPath))
{
m_ctr_edit_outpath.SetWindowText(FullPath);
lpszPathnameout = FullPath;
}
}/*------------------------------------------------------------------------------
Function: OnButtontransformClickDescription: 执行文件的转换
Input: none
Output:
Return:
------------------------------------------------------------------------------*/
void CFileToBmpbatchDlg::OnOK()
{
TCHAR CheckPath_in[MAX_PATH];
/* 检查是否有输入路径 */
if (!(m_ctr_edit_inpath.GetWindowText(CheckPath_in, sizeof(CheckPath_in))))
{
Outputmsg = "请选择数据文件!!!";
MessageBox(Outputmsg, "警告", MB_OK);
return;
if((pfin = fopen(lpszPathnamein, "rb")) == NULL)
{
MessageBox("读文件错!!!", "警告", MB_OK | MB_ICONERROR);
}
/* 单图的转换 */
if (iTransformtype == SINGLETRANS)
{
Readtext(); /* 读文件 */
SaveBmp(lpszPathnameout+Filename);
fclose(pfin);
MessageBox(Outputmsg, "提示", MB_OK);
}
/* 批量的转换 */
/* 实际上不用区分这两种转换形式,只用下面这段代码就可以了,写上为了表示清楚 */
else if (iTransformtype == BATCHTRANS)
{
/* 这里别指望feof(),永远也到不了文件结尾;也别指望fseek()能移出文件范围,
* 鸟知道怎么回事,所有图像都转完之后Readtext()里面第一个被调用的fscanf
* 由于不能正确读取就会返回-1,就用这个判断全部转换完毕
*/
while (TRUE)
{
Readtext(); /* 读文件 */
if (fileend == FILEEND)
{
fileend = 0; /* 要记得恢复状态标志,否则批量转换就只能用一次 */
break;
}
SaveBmp(lpszPathnameout+Filename);
delete [] DATAarray; /* 释放内存 */
MessageBox(Outputmsg, "提示", MB_OK);
fclose(pfin);
}
}/*------------------------------------------------------------------------------
Function: ReadtextDescription: 将颜色数据从文件中读出,并转换成24位的颜色数据
Input: none
Output:
Return: DATAarray -指向存有颜色数据的数组
Remark: 这部分是C写的,MFC用的不是很熟,谁知道哪个类的方法可以以16进制形式
从文本文件中读取数据,嘿嘿!。
------------------------------------------------------------------------------*/
void CFileToBmpbatchDlg::Readtext()
{
WORD low, high, pixel; /* 读入数据的低字节位、高字节位,pixel表示一个像素 */
RGBTRIPLE rgb; char flags;
int ifscanfstate; /* fscanf的返回值 */
int row; /* 标识行数 */
int line = 0; /* 标识列数 */
ifscanfstate = fscanf(pfin, "%s %d %d %d", \
Filename, &iBmpWidth, &iBmpHeight, &iBitcounts);
/* 就是这里,图像全部转换完毕的标志 */
if (ifscanfstate == -1)
{
fileend = FILEEND;
return;
}
/* 确保一扫描行字节数为4的整数倍 */
iRealWidth = (iBmpWidth * 3 + 3) / 4 * 4;
k = iRealWidth - iBmpWidth * 3; /* 应该补0的个数 */
row = iBmpHeight;
/* 滤去数组名等字符部分,从第一个颜色数据开始读取 */
while (true)
{
fscanf(pfin, "%c", &flags);
if (flags == '{')
{
break;
}
DATAarray = new BYTE[iRealWidth * iBmpHeight];
while (row > 0)
{
fscanf(pfin, "%x,%x,", &low, &high);
/* 数据文件中2个字节表示一个像素,且高位在后,低位在前
* 此处将像素数据正确提取
*/
high = high << 8;
pixel = low | high;
/* 将565格式位图数据扩展成888格式 */
rgb.rgbtRed = (BYTE)((pixel & 0xF800) >> 8);
rgb.rgbtGreen = (BYTE)((pixel & 0x07E0) >> 3);
rgb.rgbtBlue = (BYTE)((pixel & 0x001F) << 3);
/* 保证按正确顺序显示 */
DATAarray[(row - 1) * iRealWidth + line] = rgb.rgbtBlue;
line++;
DATAarray[(row - 1) * iRealWidth + line] = rgb.rgbtGreen;
line++;
DATAarray[(row - 1) * iRealWidth + line] = rgb.rgbtRed;
if (line == iBmpWidth * 3)
{
/* 扫描行的字节数不能被4整除时要补0对齐 */
if (k != 0)
{
for (; k > 0; k--)
{
DATAarray[(row - 1) * iRealWidth + line] = 0x00;
line++;
}
k = iRealWidth - iBmpWidth * 3; /* 千万别忘了将k的值复原,否则
* 只有第一个扫描行尾加零,图像是斜的
* 或者将k赋初值的语句放在while内
* 就不用这一句了
*/
}
if (line >= iRealWidth)
{
line = 0;
row--;
}
fseek(pfin, 7L, SEEK_CUR);/* 确保正确读取下一个图像的文件名、宽、高和位数 */
return;
}/*------------------------------------------------------------------------------
Function: SaveBmpDescription: 利用颜色数组中的数据生成BMP图像
Input: CString filename-要保存的文件名
Output: BMP文件
Return: none
------------------------------------------------------------------------------*/
void CFileToBmpbatchDlg::SaveBmp(CString filename)
{
BITMAPFILEHEADER bmfh;
BITMAPINFOHEADER bmih;
/* BITMAPFILEHEADER结构填写 */
bmfh.bfType = 0x4d42;
bmfh.bfOffBits = 54;
bmfh.bfSize = bmfh.bfOffBits + iRealWidth * iBmpHeight; /* 文件的实际大小 */
bmfh.bfReserved1 = 0;
bmfh.bfReserved2 = 0;
/* BITMAPINFOHEADER结构填写 */
bmih.biSize = 40;
bmih.biWidth = iBmpWidth;
bmih.biHeight = iBmpHeight;
bmih.biPlanes = 1;
bmih.biBitCount = iBitcounts;
bmih.biCompression = BI_RGB;
bmih.biSizeImage = 0;
bmih.biXPelsPerMeter = 0;
bmih.biYPelsPerMeter = 0;
bmih.biClrUsed = 0;
if((pfout = fopen(filename, "wb")) == NULL)
{
MessageBox("写文件错!!!", "警告", MB_OK | MB_ICONERROR);
}
/* 写入头部和像素数据 */
fwrite(&bmfh, sizeof(BITMAPFILEHEADER), 1, pfout);
fwrite(&bmih, sizeof(BITMAPINFOHEADER), 1, pfout);
fclose(pfout);
}
关于补0的问题:
已存在的图像,即使一个扫描行所占的字节数不能被4整除,它也是已经补过0的了。而本程序所读取的数据文件中不包含这些补充的零。所以,在保存颜色数据时候,如果需要补齐,必须补上,否则生成的文件是不正确的,尽管用ACDSee可以看到图像,但用XP的图片查看器是打不开的。