23
votes

I have a C#/.NET utility I wrote that loads PNG images from disk

Bitmap b = Bitmap.FromStream(new MemoryStream(File.ReadAllBytes(filename))) as Bitmap;

performs several transformations on them (rotation, scaling, alpha) and then saves the resulting PNG images back to disk with different file names based on the transformations applied

b.Save(outputName, ImageFormat.Png);

I've successfully written thousands of PNGs using the utility. However, occasionally one of the PNGs fails to load in a separate program which uses libpng. In that program, libpng gives the error "Too many IDATs found"

Looking into the PNG file reveals a 'rogue' IDAT chunk at the end of the file just before the IEND chunk. One such IDAT chunk (and the following IEND chunk) looks like this in a hex editor. These are the final 24 bytes in the file.

IDAT: 0x00 0x00 0xFF 0xF4 0x49 0x44 0x41 0x54 0x35 0xAF 0x06 0x1E    
IEND: 0x00 0x00 0x00 0x00 0x49 0x45 0x4e 0x44 0xAE 0x42 0x60 0x82

The IDAT chunk length is shown as 0xFFF4. However, as is obvious, there aren't that many bytes in the IDAT chunk (or even the file for that matter.)

Has anyone else come across this problem? I can fix the problem in one of several ways. I can hand edit the PNG file to remove that last IDAT chunk (or set its size to 0.) I can run a secondary program which fixes broken PNGs. However, I'd like a C#/.NET solution which I can easily add to my original program. Ideally, I'd like a solution which doesn't require me to re-open the PNG as a binary file; check for the bad IDAT chunk; and re-write the PNG. However, I'm beginning to think that's what I'll need to do.

2
Do your images vary in size/row stride? Have you been able to narrow down if there is something unique (width, height, stride, format) to the images that are broken?Chris
I think you're going to have to isolate the problem more. If you use C# to immediately read back each file you save, can you repro the error? If so, you need to track down the precise set of circumstances that led to that broken image and go from there.Kirk Woll
It seems other users have stumbled onto the same problem. Some code seem to recover from the problem and some don't (like libpng). Still doesn't explain where that extra chunk comes from though.Chris
I understand this is 4 years old, but am working on root causing this now. Am curious what OS you were on at the time, if you remember. Given the timeframe, presume this was Windows 7/Server 2012?Rafael Rivera
I do not have an answer to your question, but in addressing the exact same issue we found a way to reproduce it. We figured out that the size of the resulting PNG-file is a key indicator of the problem being present. If the size of the file (in bytes) is 0x1001C + n * 0x10000 with n 0, 1, 2, 3, 4 (and probably larger values, but I cannot confirm that), the problem is consistently present. I posted a question with some code to reproduce the issue: stackoverflow.com/questions/52100703/… I hope this helps.Duurt

2 Answers

2
votes

Old question.

.NET is notoriously poor at handling images. The codecs are old win32 ones with many bugs.

.NET does not always free up the OS resources used when reading/writing image files even if you follow the recommended dispose and/or using methods.

0
votes

This isn't really a complete answer since I'm unable to reproduce the image. But you could try loading the image using another one of the Bitmap constructors:

http://msdn.microsoft.com/en-us/library/0cbhe98f.aspx

Which would change your code into:

Bitmap b = new Bitmap(filename);

Because that constructor uses some native GDI+ functions to load the image into memory, instead of you reading the raw bytes:

http://dotnetframework.org/default.aspx/4@0/4@0/DEVDIV_TFS/Dev10/Releases/RTMRel/ndp/fx/src/CommonUI/System/Drawing/Bitmap@cs/1305376/Bitmap@cs

There might be a difference, even though the problem seems to be when writing the image to disk.