BMP Creation - C++ ofstream 输出损坏

Posted

技术标签:

【中文标题】BMP Creation - C++ ofstream 输出损坏【英文标题】:BMP Creation - C++ ofstream corruption on output 【发布时间】:2012-08-05 04:34:34 【问题描述】:

我目前正在为 OpenGL 框架编写 BMP 屏幕截图功能,并且在写入数据时遇到了一个奇怪的问题。从glReadPixels 引入的数据以及BMP 标头都是正确的,但似乎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
            ^^

第 0x0DC 行(此时已减一)

3E 2F 07 3D 2E 0A 3F 31 0E 44
07 3E 2F 07 3D 2E 0D 0A 3F 31
                  ^^

第 0x0E68 行(紧靠下一行,间隔两行)

35 13 48 3A 10 44 36 0A 3F 31
0E 44 35 13 48 3A 10 44 36 0D
                           ^^

所以似乎有一种模式,其中无效值始终为0x0D,并且它被插入到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;

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

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

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

【问题讨论】:

【参考方案1】:

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

要抑制这些换行符转换,您需要告诉运行时不要翻译。使用 C++ 的 iostreams,这是通过 std::ios::binary 标志完成的;对于 C 的 FILE* 流,这是通过 "b" 模式标志完成的,例如"rb" 用于阅读或"wb" 用于写作。

【讨论】:

总是很简单,不是吗?谢谢您的帮助。我想我只是需要一双新鲜的眼睛。

以上是关于BMP Creation - C++ ofstream 输出损坏的主要内容,如果未能解决你的问题,请参考以下文章

读取 BMP 文件 C++(读取 BMP 标头时出现问题)

关于怎么用C++读取bmp图片

C++:在 Windows 上使用 C++ 读写 BMP 文件的最简单方法是啥?

BMP 导出器不工作 C++

C++加密bmp图片程序

在 C++ 中读取 bmp 文件的宽度和高度