Recently I needed to determine the image dimension of graphics files in C++. I was required to support BMP, PNG and TGA files. I thought that must be something many devs need to do now and then. However, I did not find a suitable solution on the net, because I did not want to load the whole image into memory just to get the image size.
Of course the next step was to ask on Stackoverflow. With the answers provided there, I could write my own simple piece of code:
bool GetImageDimensionFromFile(const std::string& fileName, int& width, int& height) { std::ifstream in(fileName, std::ios::binary); std::uint32_t temp = 0; bool suc = false; if (!in.is_open()) { return false; } auto fmt = GetImageFormatFromFilename(fileName); if (FORMAT_BMP != fmt) { width = height = 0; in.read(reinterpret_cast<char*>(&width), 1); in.read(reinterpret_cast<char*>(&height), 1); if (width == 0x42 && height == 0x4D) { fmt = CXIMAGE_FORMAT_BMP; } } width = height = 0; switch (fmt) { case FORMAT_PNG: in.seekg(16); in.read(reinterpret_cast<char>(&width), 4); in.read(reinterpret_cast<char>(&height), 4); width = ntohl(width); height = ntohl(height); suc = true; break; case FORMAT_BMP: in.seekg(14); in.read(reinterpret_cast<char*>(&temp), 4); if (temp == 40) { // Windows Format in.read(reinterpret_cast<char>(&width), 4); in.read(reinterpret_cast<char>(&height), 4); suc = true; } else if (temp == 20) { // MAC Format in.read(reinterpret_cast<char>(&width), 2); in.read(reinterpret_cast<char>(&height), 2); suc = true; } break; case FORMAT_TGA: in.seekg(12); in.read(reinterpret_cast<char>(&width), 2); in.read(reinterpret_cast<char>(&height), 2); suc = true; break; } if (in.eof()) { return false; } return suc; }
For the ntohl function I used a function that is defined in the boost header boost/asio.hpp. The function is not in the public documentation of boost, so you may run into problems with new Boost versions. However, it is also possible to use a platform specific ntohl function or a function from another library
I have used this piece of code for quite a time for many images and so far I did not run into any problems. However, there may be some exoctic variations of an image format, where this simple solution is not sufficient…
Update 22.09.2014
Recently I came across a .PNG file which in reality is a BMP file. This file is of size 8×4 pixels, so its a pretty small image. The behavior, that imaging software like Paint.NET produces such files is reproducable. So maybe the format detection using just the extension is not that perfect. For now a made of workaround to check each file if it starts with the magic bytes of BMP (0x42 0x4D). I should probably use the magic bytes in all cases later on. However, so far this was the first problem I encountered using the function above.