Search code examples
c#image-compression

C# how to compress .png without transparent background lost?


I use the following codes to compress an image file to jpg:

// _rawBitmap = a Bitmap object

ImageCodecInfo encoder = GetEncoder(ImageFormat.Jpeg);
System.Drawing.Imaging.Encoder myEncoder = System.Drawing.Imaging.Encoder.Quality;
EncoderParameters myEncoderParameters = new EncoderParameters(1);
EncoderParameter myEncoderParameter = new EncoderParameter(myEncoder, 50L);
myEncoderParameters.Param[0] = myEncoderParameter;

ImageConverter imageConverter = new ImageConverter();
byte[] b = (byte[])imageConverter.ConvertTo(_rawBitmap, typeof(byte[]));

using (MemoryStream ms = new MemoryStream())
{
    ms.Write(b, 0, b.Length);
    ms.Seek(0, SeekOrigin.Begin);
    rawBitmap.Save(ms, encoder, myEncoderParameters);
    bmp = ToBitmap(ms.ToArray());
    return (Bitmap)bmp.Clone();
}

but when I try to compress a png file with same way but only change:

ImageCodecInfo encoder = GetEncoder(ImageFormat.Jpeg);

to

ImageCodecInfo encoder = GetEncoder(ImageFormat.Png);

my png file lost transparent data.

so how to compress a PNG file properly?


Solution

  • There are a couple of problems here. First, you don't need to set those EncoderParams for quality for PNG.

    Second, you don't need ImageConverter

    Third, you are writing whatever ImageConverter produces to your memory stream, rewinding, and then writing the encoded PNG over the top of it-- it is likely that you have a PNG file with a bunch of garbage at the end of it as a result.

    The simplified approach should be:

    using (MemoryStream ms = new MemoryStream())
    {
        rawBitmap.Save(ms, ImageFormat.Png);    
    }
    

    If you want to load your bitmap back, open it from the stream, but don't close the stream (the stream will be disposed when your returned Bitmap is disposed):

    var ms = new MemoryStream();    
    rawBitmap.Save(ms, ImageFormat.Png);    
    ms.Seek(0, SeekOrigin.Begin);
    return Bitmap.FromStream(ms);