【问题标题】:Cannot create BMP file of some sizes无法创建某些大小的 BMP 文件
【发布时间】:2018-11-21 17:50:46
【问题描述】:

我正在为我的应用程序创建小型 BMP 文件,其中一些文件(取决于像素数)使我的应用程序崩溃,并且 Windows 认为它​​们已损坏。工作尺寸的示例是 60 x 60px,但即不是 61 x 61(变量 m_width 和 m_height)。

使用的结构体(#pragma pack 应用于 BMP 相关的结构体):

struct Rgb /// vector's content
{
    uint8_t r;
    uint8_t g;
    uint8_t b;
};

#pragma pack(push, 1)
struct FileHeader
{
    int16_t bfType;
    int32_t bfSize;
    int16_t bfReserved1;
    int16_t bfReserved2;
    int32_t bfOffBits;
};

struct BitMapInfoHeader
{
    int32_t biSize;   
    int32_t biWidth;
    int32_t biHeight;
    int16_t biPlanes; 
    int16_t biBitCount;
    int32_t biCompression;
    int32_t biSizeImage; 
    int32_t biXPelsPerMeter;
    int32_t biYPelsPerMeter;
    int32_t biClrUsed;
    int8_t  biClrImportant;
    int8_t  biClrRotation;
    int16_t biReserved;
};

struct RGBQuad
{
    int8_t rgbBlue;
    int8_t rgbGreen;
    int8_t rgbRed;
    int8_t rgbReserved;
};
#pragma pack(pop)

我为整个 BMP 文件分配内存,为每个文件区域分配指针,填充结构,从其他数组复制像素数据并保存文件。代码:

    int m_width = 60, m_height = 60;

    uint8_t* data = new uint8_t[ m_width * m_height ];
    memset( data, 0, m_width * m_height );
    data[ 2 ] = 1;             /// one pixel differs

    std::vector< Rgb > RGBVec = { { 223, 223, 123 }, { 230, 0, 12 } };
    int numberOfSymbols = RGBVec.size();

    FileHeader* fileHeader;
    BitMapInfoHeader* infoHeader;
    RGBQuad* colorTable;
    uint8_t* m_pBMPFile;        /// pointer to bitmap in memory
    uint8_t* m_BMPData;         /// begin of pixel data

    int m_BMPFileLength = sizeof( FileHeader ) + sizeof( BitMapInfoHeader )
        + numberOfSymbols * sizeof( RGBQuad ) + m_width * m_height;

/// assign pointers to specific parts of bitmap:
    m_pBMPFile = new uint8_t[ m_BMPFileLength ];
    memset( m_pBMPFile, 0, m_BMPFileLength );
    fileHeader = reinterpret_cast< FileHeader* >( m_pBMPFile );
    infoHeader = reinterpret_cast< BitMapInfoHeader* >( m_pBMPFile + sizeof( FileHeader ) );
    colorTable =
        reinterpret_cast< RGBQuad* >( m_pBMPFile + sizeof( FileHeader ) + sizeof( BitMapInfoHeader ) );
    m_BMPData = reinterpret_cast< uint8_t* >( m_pBMPFile + sizeof( FileHeader ) + sizeof( BitMapInfoHeader )
        + numberOfSymbols * sizeof( RGBQuad ) );

///////////
/// FileHeader:
    fileHeader->bfType = 0x4d42;            /// magic number
    fileHeader->bfSize = m_BMPFileLength;
    fileHeader->bfOffBits = int( m_BMPData - m_pBMPFile );

/// BitMapInfoHeader:
    infoHeader->biSize = 40;
    infoHeader->biWidth = m_width;
    infoHeader->biHeight = -m_height;       /// multiplied by -1 so pixels are displayed top-down
    infoHeader->biPlanes = 1;
    infoHeader->biBitCount = 8;
    infoHeader->biCompression = 0;
    infoHeader->biSizeImage = 0;
    infoHeader->biXPelsPerMeter = 2835;
    infoHeader->biYPelsPerMeter = 2835;
    infoHeader->biClrUsed = numberOfSymbols;
    infoHeader->biClrImportant = 0;
    infoHeader->biClrRotation = 0;
/// palette:
    int i = 0;
    for( auto& s : RGBVec )
    {
        ( &colorTable[ i ] )->rgbRed = s.r;
        ( &colorTable[ i ] )->rgbGreen = s.g;
        ( &colorTable[ i ] )->rgbBlue = s.b;
        ++i;
    }

/// apply pixel data:
    memcpy( m_BMPData, data, m_width * m_height );
/// save:
    std::ofstream file2( "out.bmp", std::ios::binary | std::ios::trunc );
    file2.write( ( char* )m_pBMPFile, m_BMPFileLength );
    file2.close();

    delete[] m_pBMPFile;

在 VS2015 上编译,64 位。

【问题讨论】:

  • 你记得add padding吗?
  • 您是否通过 调试器 运行代码以确定崩溃的原因?
  • 同意轻盈。图像的每条扫描线都需要在 4 字节边界上填充。即使您的程序没有崩溃,您的 BMP 文件在查看时也很可能具有“阶梯”效果,显示扫描线已关闭。
  • 您在上面的代码中存在重大错误。首先,您将data 分配为 [Width * Height].. 但每个像素的大小可以是 3 个字节或 4 个字节。 + 你没有包括填充.. + 你没有 delete[] data.. 哦,伙计..

标签: c++ bmp


【解决方案1】:

主要问题是m_width 应该被填充,以便字节宽度可以被 4 整除。您可以使用下面的公式来保证 width_in_bytes。在这种情况下,它将宽度从 61 更改为 64。可以忽略额外的字节。

此外,您可以通过避免过度使用指针来简化代码。将标头声明为局部变量就足够了。使用std::vector 分配data 而不是new/delete。并使用可以管理数据的单个 RGBQ 标头。示例:

struct RGBQ { uint8_t rgbBlue, rgbGreen, rgbRed, rgbReserved; };

int m_width = 61, m_height = 61;
int bitcount = 8;
int width_in_bytes = ((m_width * bitcount + 31) / 32) * 4;
int imagesize = width_in_bytes * m_height;

std::vector<RGBQ> color_table{ { 223, 223, 123 }, { 230, 0, 12 } };
std::vector<uint8_t> data(imagesize);
data[2] = 1;

FileHeader fileHeader = { 0 };
BitMapInfoHeader infoHeader = { 0 };
fileHeader.bfType = 0x4d42;            
fileHeader.bfSize = sizeof(fileHeader) + sizeof(infoHeader) + color_table.size()
    * sizeof(RGBQ) + data.size();
fileHeader.bfOffBits = sizeof(fileHeader) + sizeof(infoHeader);

infoHeader.biSize = 40;
infoHeader.biWidth = m_width;
infoHeader.biHeight = -m_height;       
infoHeader.biPlanes = 1;
infoHeader.biBitCount = bitcount;
infoHeader.biClrUsed = color_table.size();

std::ofstream file2("out.bmp", std::ios::binary | std::ios::trunc);
file2.write((char*)&fileHeader, sizeof(fileHeader));
file2.write((char*)&infoHeader, sizeof(infoHeader));
file2.write((char*)color_table.data(), color_table.size() * sizeof(RGBQ));
file2.write((char*)data.data(), data.size());
file2.close();

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多