C ++保存位图文件

时间:2014-07-24 21:19:58

标签: c++ windows bitmap

所以我要做的是让我的程序截取屏幕并将其保存在计算机上。实际拍摄截图的部分我将稍后编程,我首先尝试解决如何在计算机上实际保存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;

}

那么问题是什么......我不明白变种IpBits。在代码的注释中有一个lpBits的简要说明(lpBits:指定位图位)......但我不知道这实际意味着什么。我尝试进入msdn并查看fopen和fclose函数,因为fclose是最终将使用我传递给SaveImage函数的lpbits的函数....而且似乎fclose函数中的lpBits变量依赖于在fopen函数中传递了什么变量。我试图找出&#34; wb&#34; fopen函数意味着但是没有成功(甚至在msdn上搜索)。

问题:如果我使用&#34; wb&#34;作为我之前代码中fopen函数的第二个变量,fclose函数中的lpBits究竟是什么?当我问它究竟是什么时,我的意思是......它是什么类型的变量(在代码中它被放置为void *它基本上允许它是任何变量)并且我会恭喜你可以给出的任何反馈。

谢谢你们!

3 个答案:

答案 0 :(得分:4)

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

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

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

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

您可以自己创建lpbits:

lpbits = new BYTE[lImageSize];

或使用来自Logicrat

的答案中所述的CreateDIBSection()

答案 1 :(得分:2)

评论代码:

// 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*的{​​{1}}
  • 使用vector而不是void *(可能是原始代码中的问题,如果提供的宽度和高度错误或计算错误,程序将读取无效内存,因为没有lpBits大小的概念。文件的内容保存时不需要更改,添加const-correctness
  • 使用const char*代替FILE。

代码:

std::ofstream

答案 2 :(得分:0)

研究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);
}