BMP Creation - 输出上的C++ ofstream腐败

sse*_*ell 2 c++ opengl file-io bmp

我目前正在为OpenGL框架编写BMP截图函数,并且遇到了将数据写出来的奇怪问题.从glReadPixelsBMP头引入的数据都是正确的,但似乎ofstream.write操作是随机插入无效值.

从图像的正确BMP(由Paint创建)和我的函数生成的BMP中摘录,突出显示不正确的字节.正确的行将始终是第一行.


行0x08C

3B 0D 4A 3A 0A 48 38 08 45 36
3B 0D 4A 3A 0D 0A 48 38 08 45
            ^^
Run Code Online (Sandbox Code Playgroud)

行0x0DC(此时已经关闭一个)

3E 2F 07 3D 2E 0A 3F 31 0E 44
07 3E 2F 07 3D 2E 0D 0A 3F 31
                  ^^
Run Code Online (Sandbox Code Playgroud)

行0x0E68(非常下一行,然后关闭两行)

35 13 48 3A 10 44 36 0A 3F 31
0E 44 35 13 48 3A 10 44 36 0D
                           ^^
Run Code Online (Sandbox Code Playgroud)

所以似乎有一种模式,其中无效值始终是,0x0D并且它被插入到a的前面0x0A.我不知道为什么会发生这种情况,因为我已经确认标题和数据glReadPixels都是正确的.该方法的代码如下.

bool captureScreen( const char* name, unsigned originX, unsigned originY, unsigned width, unsigned height )
{

    //...

    GLubyte* imageData = new GLubyte[ width * height * 3 ];
    glReadPixels( originX, originY, width, height, GL_BGR, GL_UNSIGNED_BYTE, imageData );

    //...

    captureAsBMP( imageData, width, height, path.c_str( ) );

    //...
}

bool captureAsBMP( GLubyte* data, GLuint width, GLuint height, const char* path )
{
    std::ofstream stream( path, std::ios_base::out );

    if( !stream.is_open( ) )
        return false;

    unsigned char BMPHeader[ 14 ] = { /* cut for brevity */ };
    unsigned char DIBHeader[ 40 ] = { /* cut for brevity */ };
    unsigned char padding[ 4 ] = { 0x00, 0x00, 0x00, 0x00 };

    unsigned int paddingLength = 4 - (( width * 3 ) % 4);
    paddingLength = ( paddingLength == 4 ? 0 : paddingLength );

    long fileSize = ( width * height * 3 ) + ( paddingLength * height ) + 54;
    long dataSize = fileSize - 54;

    memcpy( &BMPHeader[ 2 ], &fileSize, sizeof( long ) );
    memcpy( &DIBHeader[ 4 ], &width, sizeof( unsigned int ) );
    memcpy( &DIBHeader[ 8 ], &height, sizeof( unsigned int ) );
    memcpy( &DIBHeader[ 20 ], &dataSize, sizeof( long ) );

    stream.write( reinterpret_cast< char* >( BMPHeader ), 14 );
    stream.write( reinterpret_cast< char* >( DIBHeader ), 40 );

    unsigned pos = 0;

    // Write out one row at a time
    for( int i = 0; i < height; i++ )
    {
        stream.write( reinterpret_cast< char* >( &data[ pos ] ), ( width * 3 ) );

        // Is there padding that needs to be added?
        if( paddingLength != 0 )
            stream.write( reinterpret_cast< char* >( padding ), paddingLength );

        // Update where we are in data
        pos += width * 3;
    }

    stream.close( );

    return true;
}
Run Code Online (Sandbox Code Playgroud)

另外值得注意的是,所讨论的图像是800x600,因此在填充和pos递增之前,在第一次流写入行期间会发生此错误.

最后,应该如何显示:http://imgur.com/oziid

它是如何做的:http://imgur.com/SrXgA(重新保存在Paint中,因为Imgur抱怨它当然被破坏了)

Ada*_*eld 6

您需要通过添加std::ios::binary标志以二进制模式打开文件以避免CRLF转换.在Windows上,换行符表示为两个字节0x0D 0x0A(回车符+换行符或CR LF) - C和C++运行时自动将它们转换为输入上的普通换行符,并且它们'\n'在输出时自动将普通换行符转换为CRLF对,在文本模式下打开文件时,这是默认设置.

要禁止这些换行符转换,您需要告知运行时不要转换.使用C++的iostreams,这是通过std::ios::binary标志完成的; 使用C的FILE*流,这是通过"b"模式标志完成的,例如"rb"用于读取或"wb"写入.