0

Can someone explain to me why writing the stream from a PNG image back into another PNG file increases the size of the final output?

Original file: size (28.6 KB), size on disk (32.0 KB)

Output file: size (32.1 KB), size on disk (36.0 KB).

The code for doing this operation is pretty straight forward:

private void button1_Click(object sender, EventArgs e)
    {
        var result = openFileDialog1.ShowDialog();

        if (result == DialogResult.OK)
        {
            var file = openFileDialog1.FileName;
            var stream = new FileStream(file, FileMode.Open);
            var newImg = Image.FromStream(stream);
            newImg.Save("newPNG.png", ImageFormat.Png);
            stream.Close();
        }
    }

How can I avoid this? I would like the final image to have the exact same size as the original one.

LE: I uploaded the original image if anyone wants to try it out. cat image

  • 1
    Why not just copy it instead? https://msdn.microsoft.com/en-us/library/c6cfw35a(v=vs.110).aspx – JoaoFSA Apr 15 '16 at 14:21
  • Thanks for your comment. The reason for which I cannot copy it's because the project in which I am doing this saving can receive only a Stream as an input for the image. – Andrei Ionut Apr 15 '16 at 14:25
  • Id say this http://stackoverflow.com/questions/2419222/c-sharp-how-to-change-png-quality-or-color-depth is probably right – JoaoFSA Apr 15 '16 at 14:27
  • 1
    So you've `File.WriteAllBytes`. And I suggest that you read this other Q&A: http://stackoverflow.com/questions/221925/creating-a-byte-array-from-a-stream – Matías Fidemraizer Apr 15 '16 at 14:31
  • I agree with the first comment. You should not need to decode and reencode the png file. – leonbloy Apr 17 '16 at 21:16

2 Answers2

2

From Wikipedia:

There are five possible filter types that can be specified separately on each scan line and several possible strategies for searching LZ77 matches. Thus, there are a very large number of different combinations for how the image can be compressed. Which combination gives the best compression will depend on the individual image's properties.

That is, there are many ways to compress a PNG, and apparently in your case the original file was compressed in a different way from .NET's default. I'm not sure how much you can affect .NET's output, but there's an override of Image.Save that takes EncoderParameters. You might want to look at that. Link.

adv12
  • 8,443
  • 2
  • 24
  • 48
  • I believe that this is correct. Prove it by copying the copy. If the size changes on the first copy, but not subsequently, compression is the culprit. Unfortunately, .NET gives you virtually no control over compression levels in PNG files. – quest4truth Apr 15 '16 at 14:42
0

I'd bet this due to something like GDI+ saves the image with different settings, and possibly different encodings.

You can retain the same size if you create a new FileStream and read bytes from the first one, then to write them into the second one. Thus copying the file.

stream.Position = 0;
using(FileStream fs = new FileStream("newPNG.png"))
{
    int totalBytesRead = 0;
    while(totalBytesRead < stream.Length)
    {
        byte[] byteBuffer = new byte[8192];
        int bytesRead = stream.Read(readBytes, 0, byteBuffer.Length);
        fs.Write(byteBuffer, totalBytesRead, bytesRead);
        totalBytesRead += bytesRead;
    }
}
stream.Position = 0;

I set stream.Position to zero both before and after because I don't know where you'll be using this code. Setting it to zero will make the FileStream start reading from the beginning of the file.

Visual Vincent
  • 18,045
  • 5
  • 28
  • 75