views:

337

answers:

2

Hi,

I have a routine which reads in an image, resizes it and positions it on a new background (brand new bitmap, just the size is set).

This all works absolutely perfectly, but now I would like to reduce the size of the PNGs it outputs - if I output jpegs, I get the ~4K or so file size I expect, but my PNGs are more than 30K in size.

I know that I will never get to jpeg levels with PNG, but I think I can do a bit better.

When I load the outputted PNGs into Fireworks, I note that the background and the resized image are still seperate layers. Flattening the PNG in Fireworks reduces the filesize by a good 5 to 10K.

So, firstly is there anyway to programmatically flatten the PNG on output?

Secondly, is there anything else anyone can recommend to reduce the PNG size?

I am using PNGs because I wish to retain the background as transparent.

Code:

    private static void ResizeImage(String ImageInPath, int MaxWidth, int MaxHeight, String ImageOutPath, Boolean PadImage, Color MyColour)
    {
        Bitmap MyImage = new Bitmap(ImageInPath);
        Bitmap MyResizedImage = null;

        int XPosition = 0;
        int YPosition = 0;
        float Ratio = MyImage.Width / (float)MyImage.Height;

        int MyImageHeight = MyImage.Height;
        int MyImageWidth = MyImage.Width;

        if (MyImage.Width > MyImage.Height)
        {
            if (MyImage.Width > MaxWidth)
                MyResizedImage = new Bitmap(MyImage, new Size(MaxWidth, (int)Math.Round(MaxWidth /
                Ratio, 0)));
            YPosition = (MaxHeight / 2) - (MyResizedImage.Height / 2);
        }
        else if (MyImage.Height > MyImage.Width)
        {
            if (MyImage.Height > MaxHeight)
                MyResizedImage = new Bitmap(MyImage, new Size((int)Math.Round(MaxWidth * Ratio,
                0), MaxHeight));
            XPosition = (MaxWidth / 2) - (MyResizedImage.Width / 2);
        }


        if (PadImage)
        {
            Bitmap MyUnderlay = new Bitmap(MaxWidth, MaxHeight);
            var Canvas = Graphics.FromImage(MyUnderlay);
            Canvas.Clear(MyColour);
            Canvas.InterpolationMode = InterpolationMode.HighQualityBicubic;
            Canvas.DrawImage(MyResizedImage, XPosition, YPosition);
            Canvas.Save();
            if (MyColour == Color.Transparent)
            {
                MyUnderlay.Save(ImageOutPath + ".png", ImageFormat.Png);
            }
            else
            {
                MyUnderlay.Save(ImageOutPath, ImageFormat.Jpeg);
            }

            Canvas.Dispose();
            MyUnderlay.Dispose();
        }
        else
        {
            MyResizedImage.Save(ImageOutPath, ImageFormat.Jpeg);
        }

        MyResizedImage.Dispose();
        MyImage.Dispose();
    }

Regards

Moo

A: 

There's a neat program called optipng, which will squash down your PNGs to the max. If you can adapt that to your program, that'd be neat!

Chris Jester-Young
+1  A: 

The SuperUser answer, How to read Fireworks PNG multilayer files without Fireworks explains that Fireworks has png-extensions to store multiple layers in a png file. Flattening removes the extra layers. Given that the files were not created in Fireworks in the first place, in theory "Flattening" should have no effect on them. I suspect the reduction in size is due to Firework's save optimize the png.

The tools I use for png optimization are:

  1. Convert to png8: If there are few colors (say, screenshots) then I use pngnq or Gimp's Indexed color mode to quantize down to 256 colors. Png8 can be smaller than png24 or png32. For details see PNG8 – The Clear Winner.
  2. Optipng, a fast general png optimizer. C# PNG Optimization Tutorial has details on how to run optipng from C#.
  3. Finally pngout is slow but often (80-90% of the time) manages to squeeze the png down further than optipng. Run optipng first though, as optipng will automatically do other optimizations that pngout does not attempt.

If you are interested in optimizing png files, the png specification is surprisingly straight forward and it is worth taking a look at Smallest possible transparent PNG. Like the specification, implementations are also surprisingly simple, take for example, the single file, pure python implementation png.py (pypng).

Jonathan Wright
Regarding Fireworks and its extensions - the png was created by the .Net framework from a brand new bitmap and a jpeg, but when loaded into Fireworks I can still manipulate both as individual objects until I flatten everything on export from Fireworks, so either theres more here than Fireworks extensions or .Net supports them...
Moo
Moo, can the code combing the bitmap and jpg be posted?I would have thought that after drawing both the jpg and the bitmap to another bitmap that any "layer" information would be long gone, and the resulting png would be a combination of the two. Is this not the case?
Jonathan Wright
Jonathan, I have added the code :)
Moo
To be honest, I haven't ever read the PNG spec (never been *that* interested :)) but one thought I had is it might be possibly because the background is always a seperate layer in PNG, with the content overlayed above it?
Moo