JpegBitmapEncoder.Save() throws exception when writing image with metadata to MemoryStream

Posted by Dmitry on Stack Overflow See other posts from Stack Overflow or by Dmitry
Published on 2010-04-24T17:19:13Z Indexed on 2010/04/24 17:23 UTC
Read the original article Hit count: 647

Filed under:
|
|
|

I am trying to set up metadata on JPG image what does not have it. You can't use in-place writer (InPlaceBitmapMetadataWriter) in this case, cuz there is no place for metadata in image.

If I use FileStream as output - everything works fine. But if I try to use MemoryStream as output - JpegBitmapEncoder.Save() throws an exception (Exception from HRESULT: 0xC0000005). After some investigation I also found out what encoder can save image to memory stream if I supply null instead of metadata.

I've made a very simplified and short example what reproduces the problem:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;

using System.Drawing;
using System.Drawing.Imaging;
using System.Windows.Media.Imaging;

namespace JpegSaveTest
{
    class Program
    {
        public static JpegBitmapEncoder SetUpMetadataOnStream(Stream src, string title)
        {
            uint padding = 2048;
            BitmapDecoder original;
            BitmapFrame framecopy, newframe;
            BitmapMetadata metadata;
            JpegBitmapEncoder output = new JpegBitmapEncoder();
            src.Seek(0, SeekOrigin.Begin);
            original = JpegBitmapDecoder.Create(src, BitmapCreateOptions.PreservePixelFormat, BitmapCacheOption.OnLoad);
            if (original.Frames[0] != null) {
                framecopy = (BitmapFrame)original.Frames[0].Clone();
                if (original.Frames[0].Metadata != null) metadata = original.Frames[0].Metadata.Clone() as BitmapMetadata;
                else metadata = new BitmapMetadata("jpeg");
                metadata.SetQuery("/app1/ifd/PaddingSchema:Padding", padding);
                metadata.SetQuery("/app1/ifd/exif/PaddingSchema:Padding", padding);
                metadata.SetQuery("/xmp/PaddingSchema:Padding", padding);
                metadata.SetQuery("System.Title", title);
                newframe = BitmapFrame.Create(framecopy, framecopy.Thumbnail, metadata, original.Frames[0].ColorContexts);
                output.Frames.Add(newframe);
            }
            else {
                Exception ex = new Exception("Image contains no frames.");
                throw ex;
            }
            return output;
        }

        public static MemoryStream SetTagsInMemory(string sfname, string title)
        {
            Stream src, dst;
            JpegBitmapEncoder output;
            src = File.Open(sfname, FileMode.Open, FileAccess.Read, FileShare.Read);
            output = SetUpMetadataOnStream(src, title);
            dst = new MemoryStream();
            output.Save(dst);
            src.Close();
            return (MemoryStream)dst;
        }

        static void Main(string[] args)
        {
            string filename = "Z:\\dotnet\\gnom4.jpg";
            MemoryStream s;
            s = SetTagsInMemory(filename, "test title");
        }
    }
}

It is simple console application. To run it, replace filename variable content with path to any .jpg file without metadata (or use mine).

Ofc I can just save image to temporary file first, close it, then open and copy to MemoryStream, but its too dirty and slow workaround. Any ideas about getting this working are welcome :)

© Stack Overflow or respective owner

Related posts about c#

Related posts about metadata