2

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.

Jcsq6
  • 474
  • 1
  • 4
  • 15
  • 2
    I feel like the `std::ceil(image_px_width * bpp / 32)` expression is bugged because it's all integers. – dialer Sep 22 '21 at 19:00
  • Also, maybe the problem isn't the width and height per se, but rather the total file size. And even if the file size at 1bpp fits a 32 bit uint, any application that would try to construct, say, an `HBITMAP` which is compatible with the output Device Context would have it converted to 32bpp most likely. Depending on how that's programmed, that may be the limiting factor. – dialer Sep 22 '21 at 19:02
  • I agree with @dialer that converting to a floating-point number is dangerous, because it introduces the possibility of rounding errors. – Andreas Wenzel Sep 22 '21 at 19:10
  • @dialer the file size is approximately 1.2 gb, which lines up with it being 1bpp, which is why I don’t think it’s a size issue. And it was meant to be 32.0. 32 is a typo – Jcsq6 Sep 22 '21 at 19:32
  • And also, Linux’s image viewer specifically said something like “Bogus header data” which leads me to believe it’s probably something in the header. I just can’t find anything wrong in the headers – Jcsq6 Sep 22 '21 at 19:34
  • 1
    @Jcsq6 which image viewer do you use? if it is possible to find sources for it, then you can easily grep for exact error which you see – Drobot Viktor Sep 22 '21 at 19:38
  • @DrobotViktor I’ll have to check when I get to my computer which viewer I used, but it didn’t give any useful error besides “bogus header data”… the thing I don’t understand is I’ve viewed the bytes of the image numerous times meticulously and I can’t find a single thing wrong. Is a different format required for large files that I’m unaware of? – Jcsq6 Sep 22 '21 at 19:47
  • I think the limit should be imposed by 4-byte unsigned integer which used in header structs. If you believe that it's not your fault the only reason which looks good is the internal limit of image viewer. You can try to bisect this problem with the dichotomy - just generate files with somewhat large resolution and try to open. if it fails - reduce dimensions by 2 and try again. If new size will pass then find center between old and new one. Finally you should find pretty quickly (log2(N)) which size causes troubles – Drobot Viktor Sep 22 '21 at 20:16
  • 1
    Regarding your Edit 2: Well that still sounds like what I initially suggested in my second comment. A viewer might convert it to 32bpp for whatever reason, and that overflows at the very least a signed int. Worse yet if the viewer is a 32 bit application. – dialer Sep 22 '21 at 20:46
  • Does this answer your question? [Max resolution of .bmp file format](https://stackoverflow.com/questions/30941050/max-resolution-of-bmp-file-format) – Andreas Wenzel Sep 22 '21 at 21:08
  • @dialer I think you are right. I made an application to read the header data, and everything is as it should be according to the bmp format. It has to be with the image viewers. – Jcsq6 Sep 22 '21 at 21:08

1 Answers1

3

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.

benrg
  • 1,395
  • 11
  • 13
  • 1
    The maximum size allowed is the 4 byte limit in the header as far as I know – Jcsq6 Sep 22 '21 at 18:34
  • @Jcsq6 You may be right, but a lot of software just can't handle images that large regardless of the file format. Software designed to work with large raster images, like Photoshop and GIMP, may be able to open the files. – benrg Sep 22 '21 at 18:40
  • I’ve tried opening it in photoshop, Ubuntu’s native image viewer, window’s native image viewer, and gimp. If it was a size problem, I can see the native viewers struggling. But photoshop or gimp should be able to handle it… so I don’t think that’s the problem, but I may be wrong – Jcsq6 Sep 22 '21 at 18:51
  • 1
    @Jcsq6: I suggest that you create several bitmaps of different sizes and try to load them in all of the mentioned viewers. Then you can compare which bitmaps load inside which program and which ones don't. If different viewers have different behavior, then the problem is likely in the viewers. However, if the behavior is consistent with all viewers, then the problem is likely in the bitmap files. – Andreas Wenzel Sep 22 '21 at 19:51
  • @AndreasWenzel alright I'll try that – Jcsq6 Sep 22 '21 at 19:54