Search code examples
c#.net-corecross-platformsdlsdl-2

Finding PNG image width/height via file metadata .NET Core 3.1 C#


I'm using .NET Core 3.1 with C#, and am using SDL2 (including SDL2_image).

I need to find a way to fetch the width and height of an image file before loading it using SDL2_image.IMG_Load(Filename), as this crashes if the dimensions of the image exceed the maximum texture size of the GPU.

All solutions I've seen for this were either for JPEG, or used System.Drawing for .NET Core. Needing to reference a massive library for something as trivial as fetching image width and height seems absurd to me, so I figured I might be able to read them from the file's metadata.

Are there any light-weight, cross-platform solutions (that don't require a big library with many other intended uses) for finding the width/height of an image file?


Solution

  • Thanks to Jimi and Retired Ninja, I was able to figure out how to get width/height from a PNG file.

    It's actually very simple. PNG files have a very clearly defined structure as pointed out here by Retired Ninja).

    I'm looking for image width and height, which comes in a "chunk" after the 8-byte long header. Since I happen to know for certain that my image format will be PNG and that the file is valid, I can skip all validation and verification and skip straight to the data itself: width at bytes 16 - 20 and height at 20 - 24 (bytes 8 - 12 are the chunk name; IHDR, a special chunk type, and 12 - 16 the length of the chunk; 0, probably because it's a special and fixed-size chunk). I'd recommend to check out the wikipedia page linked above to understand how the format works, it really made it pretty simple. I also opened one of my PNG files in a Hex Editor to compare a real example with the PNG format as described on wikipedia to make it easier to understand.

    As such, I was able to get it to work properly with this code:

        public Size GetImageSize(string Filename)
        {
            BinaryReader br = new BinaryReader(File.OpenRead(Filename));
            br.BaseStream.Position = 16;
            byte[] widthbytes = new byte[sizeof(int)];
            for (int i = 0; i < sizeof(int); i++) widthbytes[sizeof(int) - 1 - i ] = br.ReadByte();
            int width = BitConverter.ToInt32(widthbytes, 0);
            byte[] heightbytes = new byte[sizeof(int)];
            for (int i = 0; i < sizeof(int); i++) heightbytes[sizeof(int) - 1 - i] = br.ReadByte();
            int height = BitConverter.ToInt32(heightbytes, 0);
            return new Size(width, height);
        }