C ++保存一个位图文件

所以我想要做的是让我的程序截图并保存在电脑上。 实际上截图的部分我会稍后编程,而我首先试图解决如何在计算机上实际保存bmp文件的问题。 我发现下面的代码可以帮助我:

// szPathName : Specifies the pathname // lpBits : Specifies the bitmap bits // w : Specifies the image width // h : Specifies the image height bool SaveImage(char* szPathName, void* lpBits, int w, int h) { //Create a new file for writing FILE *pFile = fopen(szPathName, "wb"); if(pFile == NULL) { return false; } BITMAPINFOHEADER BMIH; BMIH.biSize = sizeof(BITMAPINFOHEADER); BMIH.biSizeImage = w * h * 3; // Create the bitmap for this OpenGL context BMIH.biSize = sizeof(BITMAPINFOHEADER); BMIH.biWidth = w; BMIH.biHeight = h; BMIH.biPlanes = 1; BMIH.biBitCount = 24; BMIH.biCompression = BI_RGB; BMIH.biSizeImage = w * h* 3; BITMAPFILEHEADER bmfh; int nBitsOffset = sizeof(BITMAPFILEHEADER) + BMIH.biSize; LONG lImageSize = BMIH.biSizeImage; LONG lFileSize = nBitsOffset + lImageSize; bmfh.bfType = 'B'+('M'<<8); bmfh.bfOffBits = nBitsOffset; bmfh.bfSize = lFileSize; bmfh.bfReserved1 = bmfh.bfReserved2 = 0; //Write the bitmap file header UINT nWrittenFileHeaderSize = fwrite(&bmfh, 1, sizeof(BITMAPFILEHEADER), pFile); //And then the bitmap info header UINT nWrittenInfoHeaderSize = fwrite(&BMIH, 1, sizeof(BITMAPINFOHEADER), pFile); //Finally, write the image data itself //-- the data represents our drawing UINT nWrittenDIBDataSize = fwrite(lpBits, 1, lImageSize, pFile); fclose(pFile); return true; } 

那么是什么问题….嗯,我不明白varialbe IpBits。 在代码的注释中有一个lpBits的简要解释(lpBits:指定位图位)…但我不知道这实际上是什么意思。 我试图进入msdn,看着fopen和fclose函数,因为fclose是最终将使用我传递给SaveImage函数的lpbits的函数….而且好像fclose函数中的lpBitsvariables依赖于在fopen函数中传递了什么variables。 我试图找出fopen函数的“wb”是什么意思,但不成功(甚至在msdn上search)。

问题:在我以前的代码中使用“wb”作为fopen函数中的第二个variables的情况下,fclose函数中的lpBits究竟是什么? 当我问到究竟是什么时,我的意思是……它是什么types的variables(在代码中它被放置为void *,它基本上允许它是任何variables),我会appriciate任何反馈,你可以给。

多谢你们!

lpBits指的是一个大小为lImageSize的字节数组。

数组的每个字节都将包含一个单一的颜色分量,顺序如下:B,G和R:每个像素需要三个字节,每个颜色分量一个。

请注意,您发布的代码没有考虑到每个图像行的4字节对齐。 每个图像的行必须在4字节边界上对齐,所以lImageSize的正确公式为:

 lImageSize = h * ((w * 3 + 3) & 0xfffffffc); 

你可以自己创建lpbits:

 lpbits = new BYTE[lImageSize]; 

或者按照Logicrat的回答中所述使用CreateDIBSection()

评论代码:

 // lpBits stand for long pointer bits // szPathName : Specifies the pathname -> the file path to save the image // lpBits : Specifies the bitmap bits -> the buffer (content of the) image // w : Specifies the image width // h : Specifies the image height bool SaveImage(char* szPathName, void* lpBits, int w, int h) { // Create a new file for writing FILE* pFile = fopen(szPathName, "wb"); // wb -> w: writable b: binary, open as writable and binary if (pFile == NULL) { return false; } BITMAPINFOHEADER BMIH; // BMP header BMIH.biSize = sizeof(BITMAPINFOHEADER); BMIH.biSizeImage = w * h * 3; // Create the bitmap for this OpenGL context BMIH.biSize = sizeof(BITMAPINFOHEADER); BMIH.biWidth = w; BMIH.biHeight = h; BMIH.biPlanes = 1; BMIH.biBitCount = 24; BMIH.biCompression = BI_RGB; BMIH.biSizeImage = w * h * 3; BITMAPFILEHEADER bmfh; // Other BMP header int nBitsOffset = sizeof(BITMAPFILEHEADER) + BMIH.biSize; LONG lImageSize = BMIH.biSizeImage; LONG lFileSize = nBitsOffset + lImageSize; bmfh.bfType = 'B' + ('M' << 8); bmfh.bfOffBits = nBitsOffset; bmfh.bfSize = lFileSize; bmfh.bfReserved1 = bmfh.bfReserved2 = 0; // Write the bitmap file header // Saving the first header to file UINT nWrittenFileHeaderSize = fwrite(&bmfh, 1, sizeof(BITMAPFILEHEADER), pFile); // And then the bitmap info header // Saving the second header to file UINT nWrittenInfoHeaderSize = fwrite(&BMIH, 1, sizeof(BITMAPINFOHEADER), pFile); // Finally, write the image data itself //-- the data represents our drawing // Saving the file content in lpBits to file UINT nWrittenDIBDataSize = fwrite(lpBits, 1, lImageSize, pFile); fclose(pFile); // closing the file. return true; } 

用C ++代替C代码的一些改进:

改进是:

  • 使用std::string而不是char* ,最初需要是const char*
  • 使用vector而不是void *(可能是原始代码中的一个问题,如果提供的宽度和高度是错误的或者计算错误,程序将读取无效的内存,因为没有lpBits的大小的概念。文件的内容不需要保存时更改,添加const正确性
  • 使用std::ofstream而不是FILE。

码:

 // lpBits stand for long point bits // szPathName : Specifies the pathname -> the file path to save the image // lpBits : Specifies the bitmap bits -> the buffer (content of the) image // w : Specifies the image width // h : Specifies the image height bool SaveImage(const std::string& szPathName, const std::vector<char>& lpBits, int w, int h) { // Create a new file for writing std::ofstream pFile(szPathName, std::ios_base::binary); if (!pFile.is_open()) { return false; } BITMAPINFOHEADER bmih; bmih.biSize = sizeof(BITMAPINFOHEADER); bmih.biWidth = w; bmih.biHeight = h; bmih.biPlanes = 1; bmih.biBitCount = 24; bmih.biCompression = BI_RGB; bmih.biSizeImage = w * h * 3; BITMAPFILEHEADER bmfh; int nBitsOffset = sizeof(BITMAPFILEHEADER) + bmih.biSize; LONG lImageSize = bmih.biSizeImage; LONG lFileSize = nBitsOffset + lImageSize; bmfh.bfType = 'B' + ('M' << 8); bmfh.bfOffBits = nBitsOffset; bmfh.bfSize = lFileSize; bmfh.bfReserved1 = bmfh.bfReserved2 = 0; // Write the bitmap file header pFile.write((const char*)&bmfh, sizeof(BITMAPFILEHEADER)); UINT nWrittenFileHeaderSize = pFile.tellp(); // And then the bitmap info header pFile.write((const char*)&bmih, sizeof(BITMAPINFOHEADER)); UINT nWrittenInfoHeaderSize = pFile.tellp(); // Finally, write the image data itself //-- the data represents our drawing pFile.write(&lpBits[0], lpBits.size()); UINT nWrittenDIBDataSize = pFile.tellp(); pFile.close(); return true; } 

学习Windows API CreateDIBSection() 。 有了这个API,Windows将为您需要的像素分配内存。 当它分配内存时,它会给你一个长指针的内存地址。 这就是“lpbits”所指的 – 指向已分配位的长指针。

fopen()的“wb”表示“写入二进制文件”。 如果没有“b”(即,如果在第二个参数中只使用“w”), fopen将以文本模式打开文件,这将导致文件被写入,就像文本一样,这可能会改变“ \ n“字符以系统依赖的方式。

这里是我用于PixMapAny类的构造函数,它主要用于离屏绘制,但也可用于读取像素图。

 PixMapAny::PixMapAny(int width, int height, int depth) { m_dc.CreateCompatibleDC(NULL); m_width = width; m_height = height; m_depth = depth; // The declaration of 'fake' creates a storage area big enough to // contain a BITMAPINFO structure composed of a BITMAPINFOHEADER // and a 256-element array of RGBQUAD values. long fake[266]; LPBITMAPINFO pbmi = (LPBITMAPINFO) fake; // Initialize the area to all zeros for(int x = 0; x < 266; x++) fake[x] = 0; // Fill in the header with the characteristics of the bitmap we want // to write. pbmi->bmiHeader.biSize = sizeof(pbmi->bmiHeader); pbmi->bmiHeader.biWidth = m_width; pbmi->bmiHeader.biHeight = -m_height; pbmi->bmiHeader.biPlanes = 1; pbmi->bmiHeader.biBitCount = 24; pbmi->bmiHeader.biCompression = BI_RGB; // Tell the system to allocate room for the pixmap. // 'ppvbits' receives a pointer to the pixmap memory. m_dib = CreateDIBSection(m_dc.m_hDC, pbmi, DIB_RGB_COLORS, &m_ppvbits, NULL, 0); // ____________________________________________________________________________ // Select the bitmap into the device context m_prev = (CBitmap *) m_dc.SelectObject(m_dib); // ____________________________________________________________________________ } 

在这个例子中,高度是负的,因为像素图中的行将以自顶向下的方式排序,即顶行的地址小于底行的地址。

一旦以这种方式构建了像素图,复制到打开的窗口的区域是容易的。 这里,pDC是一个指向目标窗口设备上下文的指针,x和y是该窗口内的坐标:

 void PixMapAny::Blit(int x, int y, CDC * pDC) { pDC->BitBlt(x,y,m_width,m_height,&m_dc,0,0,SRCCOPY); }