读数据文件 生成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的图片查看器是打不开的。