Home > Net >  Large generated BMP file corrupted
Large generated BMP file corrupted

Time:09-23

I've created something to generate large BMP files. It works great until the BMP file gets large. For some reason, it is unable to be displayed on both windows and Linux due to "Invalid header data". I don't understand, because it is well within the numerical bounds of a BMP file's header, and it works for small files perfectly find. My best guess is some type of data overflow, but I can't find it anywhere. I've even inspected the binary data of the resultant BMP file, and everything seems to be where it should be! Anyway, here's my code, and I'm going to link a couple example files-- one that works and one that doesn't. (The BMP file is one bit per pixel).

void gen_headers(std::fstream& file, size_t image_px_width, size_t image_px_height) {
    constexpr size_t header_size = 14;
    constexpr size_t info_header_size = 40;
    constexpr size_t color_table_size = 8;

    constexpr size_t image_offset = header_size   info_header_size   color_table_size;

    constexpr size_t bpp = 1;

    size_t row_width = static_cast<size_t>(std::ceil(image_px_width / 32.0) * 4);
    size_t image_size = row_width * image_px_height;
    size_t file_size = image_offset   image_size;

    unsigned char file_header[header_size] = { 0 };
    unsigned char info_header[info_header_size] = { 0 };
    unsigned char color_table[color_table_size] = { 0 };

    //header file type
    file_header[0] = 'B';
    file_header[1] = 'M';


    //header file size info
    file_header[2] = static_cast<unsigned char>(file_size);
    file_header[3] = static_cast<unsigned char>(file_size >> 8);
    file_header[4] = static_cast<unsigned char>(file_size >> 16);
    file_header[5] = static_cast<unsigned char>(file_size >> 24);

    //header offset to pixel data
    file_header[10] = image_offset;

    //info header size
    info_header[0] = info_header_size;

    //info header image width
    info_header[4] = static_cast<unsigned char>(image_px_width);
    info_header[5] = static_cast<unsigned char>(image_px_width >> 8);
    info_header[6] = static_cast<unsigned char>(image_px_width >> 16);
    info_header[7] = static_cast<unsigned char>(image_px_width >> 24);

    //info header image height
    info_header[8] = static_cast<unsigned char>(image_px_height);
    info_header[9] = static_cast<unsigned char>(image_px_height >> 8);
    info_header[10] = static_cast<unsigned char>(image_px_height >> 16);
    info_header[11] = static_cast<unsigned char>(image_px_height >> 24);

    //info header planes
    info_header[12] = 1;

    //info header bits per pixel
    info_header[14] = static_cast<unsigned char>(bpp);

    //image size
    info_header[20] = static_cast<unsigned char>(image_size);
    info_header[21] = static_cast<unsigned char>(image_size >> 8);
    info_header[22] = static_cast<unsigned char>(image_size >> 16);
    info_header[23] = static_cast<unsigned char>(image_size >> 24);

    //resolution
    info_header[24] = 1;
    info_header[28] = 1;

    //color table white
    color_table[4] = 255;
    color_table[5] = 255;
    color_table[6] = 255;

    file.seekp(0);
    file.write((char*)file_header, header_size);
    file.write((char*)info_header, info_header_size);
    file.write((char*)color_table, color_table_size);
}
std::fstream file(name, std::fstream::binary | std::fstream::in | std::fstream::out | std::fstream::trunc);

gen_headers(file, image_width, image_height);

size_t row_width = static_cast<size_t>(std::ceil(image_width / 32.0) * 4);
char* row = new char[row_width];
std::fill(row, row   row_width, 255);
for (size_t y = 0; y < image_height;   y) {
    file.write(row, row_width);
}

delete[] row;

EDIT: I've stripped the image generation down to its bare bones for clarity. The same problem occurs.
EDIT 2: On my computer the threshold between a bmp being able to be viewed is between 23,000 by 23,000 (64,598kb), and 24,000 by 24,000 (70,313kb). Also worth noting: when looking at image properties in file explorer, if the file is "corrupt", windows can't determine the width, height, or depth of the image. Another reason why I beleive it's something in the headers.
EDIT 3:
After writing a quick application to read the header data, I've confirmed there is nothing wrong with the headers. No overflow or failure in any way. I guess the solution is to not use BMP.

CodePudding user response:

Your second image is 100001×100001, i.e., 10 gigapixels. While I can't find an official statement online about the maximum allowed size of a BMP, it's likely that a lot of software limits the width and height to 32767 or 65535, or expands bilevel images to one byte per pixel internally and limits the size of that internal representation to a few gigabytes.

  • Related