优秀的编程知识分享平台

网站首页 > 技术文章 正文

Windows API操作BMP图像(5)保存BMP图像

nanyue 2024-08-16 19:59:29 技术文章 10 ℃

如果是保存字节对齐的24位彩色图像,操作相对简单,只用依次写入文件头,信息头和图像像素即可,如下列代码中函数SaveRectClrBmpImg()所示。如果是保存8位灰度图像,则需要再额外写入调色板信息,如代码中函数SaveGrayBmpImg()所示。这里对256位灰度图像的调色板的定义是red=green=blue分量,从0到255一共256个调色板分量。

保存图像时需要考虑图像的4字节对齐问题,对齐的方法是在每行像素的末尾补0。在 SaveGrayBmpImg()函数中假设输入图像数据是4字节对齐的,不用补0,如果图像数据不是4字节对齐的,则需要进行补0操作;在SaveRectClrBmpImg()函数中要在图像中间截取图像的一部分,要根据需要在行末尾补0。

保存BMP图像

//功能:从24位真彩色图像中截取一部分并写到文件中
//输入:szFileName,文件名;
//输入:pBmpBits,图像像素指针,每行像素字节数是4对齐的
//输入:nWidth,图像宽度;nHeight,图像高度,可以是负数
//输入:rectCut,原图像中截取的矩形区域
//输出:保存成功,返回true,保存失败,返回false
bool SaveRectClrBmpImg(char *szFileName, const unsigned char  *pBmpBits,
     int nWidth, int nHeight, CRect rectCut)
{
  FILE *pFile = NULL;
  if (fopen_s(&pFile, szFileName, "wb")) {
    std::cout << "打开文件失败" << std::endl;
    return false;
  }
  int nDstWidth = rectCut.Width(); //截取图像宽度
  int nDstHeight = rectCut.Height(); //截取图像高度
  BITMAPFILEHEADER bmfHeader; //文件头对象
  bmfHeader.bfType = DIB_HEADER_MARKER; //'BM'
  bmfHeader.bfOffBits = sizeof(BITMAPFILEHEADER) + 
       sizeof(BITMAPINFOHEADER); //真彩色图像没有调色板
  bmfHeader.bfSize = bmfHeader.bfOffBits + 
        nDstHeight*WIDTHBYTES(nDstWidth*24); //BMP文件大小
  //Y坐标要根据情况进行转换
  int nLeft = rectCut.left;
  int nRight = rectCut.right;
  int nTop = 0, nBot = 0;
  if (nHeight > 0) {
    nTop = nHeight - rectCut.bottom;
    nBot = nHeight - rectCut.top;
  }
  else {
    nTop = rectCut.top;
    nBot = rectCut.bottom;
    nDstHeight = -nDstHeight;
  }
  //写入文件头
  if (fwrite(&bmfHeader, sizeof(BITMAPFILEHEADER),1,pFile)!= 1){
    std::cout << "写文件头失败" << std::endl;
    fclose(pFile);
    return false;
  }
  //BMP信息头
  BITMAPINFOHEADER infoHeader;
  memset(&infoHeader, 0, sizeof(BITMAPINFOHEADER));
  infoHeader.biBitCount = 24;
  infoHeader.biHeight = nDstHeight;
  infoHeader.biWidth = nDstWidth;
  infoHeader.biPlanes = 1;
  infoHeader.biSize = sizeof(BITMAPINFOHEADER); //40
  //写入信息头
  if (fwrite(&infoHeader, sizeof(BITMAPINFOHEADER), 1,pFile)!= 1){
    std::cout << "写信息头失败" << std::endl;
    fclose(pFile);
    return false;
  }
  //原图像每行字节数
  int nSrcLineBytes = WIDTHBYTES(nWidth * 24);
  //目标图像每行字节数
  int nDstLineBytes = WIDTHBYTES(nDstWidth * 24);
  //目标图像每行实际像素所占用的字节数
  int nPixelBytes = nDstWidth * 3;
  //需要填充的字节数
  int nPadSize = nDstLineBytes - nPixelBytes;
  unsigned char padArray[3] = { 0 };
  //写入图像像素数据
  for (int y = nTop; y < nBot; y++) {
    unsigned char *pSrcPtr = pBmpBits + \
    y*nSrcLineBytes + nLeft * 3;
    if (fwrite(pSrcPtr, nPixelBytes, 1, pFile) != 1) {
    std::cout << "写入一行数据失败" << std::endl;
    fclose(pFile);
    return false;
  }
  if (nPadSize > 0) //在每行的末尾填0以保证字节对齐
  	fwrite(padArray, nPadSize, 1, pFile);
  }
  fclose(pFile);
  return true;
}


//功能:保存8位灰度图像
//输入:szFileName,文件名;
//输入:pBmpBits,图像像素指针,每行像素字节数是4对齐的
//输入:nWidth,图像宽度;nHeight,图像高度
//输出:保存成功,返回true;保存失败,返回false
bool SaveGrayBmpImg(char *szFileName, unsigned char *pBmpBits, 
     int nWidth, int nHeight)
{
  FILE *pFile = NULL;
  if (fopen_s(&pFile, szFileName, "wb") != 0) {
    std::cout << "打开文件失败" << std::endl;
    return false;
  }
  BITMAPFILEHEADER bmfHeader; //文件头对象
  bmfHeader.bfType = DIB_HEADER_MARKER;
  bmfHeader.bfOffBits = sizeof(BITMAPFILEHEADER) + 
      sizeof(BITMAPINFOHEADER) + 256 * sizeof(RGBQUAD);
  int nLineBytes = WIDTHBYTES(nWidth * 8); //每行字节数
  bmfHeader.bfSize = bmfHeader.bfOffBits + nLineBytes*abs(nHeight);
  //写入文件头
  if (fwrite(&bmfHeader, sizeof(BITMAPFILEHEADER), 1,pFile)!= 1){
    std::cout << "写入文件头失败" << std::endl;
    fclose(pFile);
    return false;
  }
  //BMP信息头
  BITMAPINFOHEADER infoHeader;
  memset(&infoHeader, 0, sizeof(BITMAPINFOHEADER));
  infoHeader.biBitCount = 8;
  infoHeader.biHeight = nHeight;
  infoHeader.biWidth = nWidth;
  infoHeader.biPlanes = 1;
  infoHeader.biClrUsed = 256;
  infoHeader.biClrImportant = 0;
  infoHeader.biSize = sizeof(BITMAPINFOHEADER);
  //写入信息头
  if (fwrite(&infoHeader, sizeof(BITMAPINFOHEADER),1,pFile) != 1){
    std::cout << "写入信息头失败" << std::endl;
    fclose(pFile);
    return false;
  }
  //写入调色板
  RGBQUAD pal;
  for (int i = 0; i < 256; i++) {
    pal.rgbBlue = i;
    pal.rgbGreen = i;
    pal.rgbRed = i;
    pal.rgbReserved = 0;
    if (fwrite(&pal, sizeof(RGBQUAD), 1, pFile) != 1){
      std::cout << "写入调色板失败" << std::endl;
      fclose(pFile);
      return false;
    }
  }
  //写入图像像素
  if (fwrite(pBmpBits, nLineBytes*abs(nHeight), 1, pFile) != 1){
    std::cout << "写入像素值失败" << std::endl;
    fclose(pFile);
    return false;
  }
  fclose(pFile);
  return true;
}

Tags:

最近发表
标签列表