views:

38

answers:

1

Hello All,

I am attempting to resize and save an image, which is fairly easy (for instance, see this example).

However, using this code strips the metadata information from the image. I can't quite seem to figure out how to preserve the metadata for a jpeg image.

*EDIT: Example Code *

    public static void ResizeMethodThree(string sourceFile, string targetFile)
    {
        byte[] baSource = File.ReadAllBytes(sourceFile);

        PropertyItem[] propertyItems = new Bitmap(sourceFile).PropertyItems;

        using (Stream streamPhoto = new MemoryStream(baSource))
        {
            BitmapFrame bfPhoto = ReadBitmapFrame(streamPhoto);
            BitmapMetadata metaData = (BitmapMetadata)bfPhoto.Metadata;
            int nNewPictureSize = 200;
            int nWidth = 0;
            int nHeight = 0;

            if (bfPhoto.Width > bfPhoto.Height)
            {
                nWidth = nNewPictureSize;
                nHeight = (int)(bfPhoto.Height * nNewPictureSize / bfPhoto.Width);
            }
            else
            {
                nHeight = nNewPictureSize;
                nWidth = (int)(bfPhoto.Width * nNewPictureSize / bfPhoto.Height);
            }        

            BitmapFrame bfResize = ResizeHelper(bfPhoto, nWidth, nHeight, BitmapScalingMode.HighQuality);

            byte[] baResize = ToByteArray(bfResize);

            File.WriteAllBytes(targetFile, baResize);

            Image targetImage = new Bitmap(targetFile);
            foreach (var propertyItem in propertyItems)
            {
                targetImage.SetPropertyItem(propertyItem);
            }

            targetImage.Save(targetFile);
        }
    }

    public static BitmapFrame ResizeHelper(BitmapFrame photo, int width, 
                                           int height, BitmapScalingMode scalingMode)
    {

        var group = new DrawingGroup();
        RenderOptions.SetBitmapScalingMode(
            group, scalingMode);
        group.Children.Add(
            new ImageDrawing(photo,
                new Rect(0, 0, width, height)));
        var targetVisual = new DrawingVisual();
        var targetContext = targetVisual.RenderOpen();
        targetContext.DrawDrawing(group);
        var target = new RenderTargetBitmap(
            width, height, 96, 96, PixelFormats.Default);
        targetContext.Close();
        target.Render(targetVisual);
        var targetFrame = BitmapFrame.Create(target);
        return targetFrame;
    }

private static byte[] ToByteArray(BitmapFrame bfResize)
{
    using (MemoryStream msStream = new MemoryStream())
    {
        JpegBitmapEncoder jpgEncoder = new JpegBitmapEncoder();
        jpgEncoder.Frames.Add(bfResize);
        jpgEncoder.Save(msStream);
        return msStream.ToArray();
    }
}

private static BitmapFrame ReadBitmapFrame(Stream streamPhoto)
{
    BitmapDecoder bdDecoder = 
        BitmapDecoder.Create(streamPhoto, BitmapCreateOptions.PreservePixelFormat, BitmapCacheOption.None);
    return bdDecoder.Frames[0];
}

Thanks, wTs

+1  A: 

Use the Image.PropertyItems property on the source image to get the list of metadata items. Loop through the list, calling Image.SetPropertyItem on the destination image. You should normally avoid resizing and re-compressing a jpeg image, working from an uncompressed original is best to maintain quality and avoid artifacts.

Hans Passant
@Hans - thank you for the answer. FYI - The resizing is just a way for us to increase efficiency in an application where quality isn't an issue (although this will be tested) - we are preserving the original files as well.
Wonko the Sane
A jpeg image that's resized to make it smaller and saved back often takes *more* space.
Hans Passant
Maybe if you use targetImage.SetPropertyItem() instead of targetFile. Expecting me to diagnose your problem from this is not realistic.
Hans Passant
Previous comment deleted because of type. I am editing my question to show what I am attempting.
Wonko the Sane
That's an ugly mix of WPF and GDI+ code. It doesn't work because you re-save the image in the PNG format. You have to pass the ImageFormat.
Hans Passant
+1 for ugly mix - I'm not so happy about it myself. I'd love to find (but have been unsuccessful thus far) finding a true WPF example.
Wonko the Sane