Scott Hanselman

Dealing with Images with Bad Metadata - Corrupted Color Profiles in WPF

July 30, '10 Comments [4] Posted in Bugs | Windows Client | WPF
Sponsored By

Creating a Twitter client is a really interesting exercise in application development because, amongst many reasons, it's taking input from effectively an infinite number of people and places. Never trust user input, right? Input to your application comes not only in the form of text, but also images. Writing a Twitter client is effectively writing a web browser that only browses one website. Getting a browser stable is hard.

Long Zheng, Raphael Rivera and the MetroTwit team (MetroTwit is a lovely new Twitter client) have hit an extremely interesting crashing bug. The input comes in the form of a corrupted JPG image from the web.

Here's the bad image. Looks like a picture some folks speaking on a panel. However, even though this image looks fine, this specific binary version of it has a corrupted Color Profile.

Sometimes folks don't realize that image formats contain lots of metadata that you can't see. Your JPGs may show what camera you used, what lens, what settings, possibly even the geo-coordinates of where you took the picture!

You can view all this extended information (EXIF) with a number of tools. A great free one is ExifTool by Phil Harvey at the command line, or a non-command line one like ExifPro. Windows Live Photo Gallery lets you view the data also.

Here's a snippet of some of the info in this pic:

Device Mfg Desc                 : IEC http://www.iec.ch
Device Model Desc               : IEC 61966-2.1 Default RGB colour space - sRGB
Viewing Cond Desc               : Reference Viewing Condition in IEC61966-2.1
Viewing Cond Illuminant         : 19.6445 20.3718 16.8089
Viewing Cond Surround           : 3.92889 4.07439 3.36179
Viewing Cond Illuminant Type    : D50
Make                            : Leica Camera AG
Camera Model Name               : M8 Digital Camera
Software                        : Aperture 3.0.2
Shutter Speed Value             : 1/256
Exposure Compensation           : 0
Max Aperture Value              : 1.0
Metering Mode                   : Center-weighted average
Light Source                    : Flash
Focal Length                    : 0.0 mm

You can extract the image profile (ICC Profile) from an image like this with exiftool:

exiftool -icc_profile -b foo.jpg > profile.icc

If you're hardcore, you can get the Windows Imaging Component (WIC) Tools and run WICExplorer. WPF uses WIC to decode images. WICExplorer will report the error with this image as you load it.

Loading Images in WPF

When you're using WPF (Windows Presentation Foundation) to display an image on Windows, you might do something like this:

<Image Width="300" Height="300" ImageFailed="Image_ImageFailed">
<Image.Source>
<BitmapImage UriSource="http://hanselman.com/blog/images/JPGwithBadColorProfile.jpg"/>
</Image.Source>
</Image>

Except with this particular image, I'll get an exception the Color Profile (the image metadata) is corrupted. "ArgumentException: Value does not fall within the expected range." This is a corrupted file.

at System.Windows.Media.ColorContext.GetColorContextsHelper(GetColorContextsDelegate getColorContexts)
at System.Windows.Media.Imaging.BitmapFrameDecode.get_ColorContexts()
at System.Windows.Media.Imaging.BitmapImage.FinalizeCreation()
at System.Windows.Media.Imaging.BitmapImage.OnDownloadCompleted(Object sender, EventArgs e)
at System.Windows.Media.UniqueEventHelper.InvokeEvents(Object sender, EventArgs args)
at System.Windows.Media.Imaging.LateBoundBitmapDecoder.DownloadCallback(Object arg)
at System.Windows.Threading.ExceptionWrapper.InternalRealCall(Delegate callback, Object args, Int32 numArgs)
at MS.Internal.Threading.ExceptionFilterHelper.TryCatchWhen(Object source, Delegate method, Object args, Int32 numArgs, Delegate catchHandler)

If I get this exception, I can try to load the image again and ignore its color profile. Here's how I'd do that in XAML:

<Image Width="300" Height="300" ImageFailed="Image_ImageFailed"  >
<Image.Source>
<BitmapImage CreateOptions="IgnoreColorProfile" UriSource="http://hanselman.com/blog/images/JPGwithBadColorProfile.jpg"/>
</Image.Source>
</Image>

If you're loading from code, you can ignore color profile information by adding the BitmapCreateOptions.IgnoreColorProfile flag to CreateOptions.

As an aside, Andrew Eichacker has a nice post on how to read all the BitmapMetadata in WPF. There's lots in there!

Here's loading the Bitmap into an image Control called "Foo."

var bi = new BitmapImage();
bi.BeginInit();
bi.CreateOptions = BitmapCreateOptions.IgnoreColorProfile;
bi.UriSource = new Uri("http://hanselman.com/blog/images/JPGwithBadColorProfile.jpg");
bi.EndInit();

foo.Source = bi;

Knowing about possible corruption is important to be aware of, especially if you're loading arbitrary images from all over the place. If you don't care about color profiles, I'd just ignore them by default in your image loading code. If you are writing an image editor or you care about profiles, I'd catch the exception, let the user know, then load the image again without the profile.

About Scott

Scott Hanselman is a former professor, former Chief Architect in finance, now speaker, consultant, father, diabetic, and Microsoft employee. I am a failed stand-up comic, a cornrower, and a book author.

facebook twitter subscribe
About   Newsletter
Sponsored By
Hosting By
Dedicated Windows Server Hosting by ORCS Web
Saturday, July 31, 2010 8:41:22 AM UTC
Interesting - testing this with Silverlight it turns out that the BitmapCreateOptions enumeration for Silverlight is not the same as the WPF enumeration - there are fewer values and specifically no IgnoreColorProfile value. Silverlight does seem to load this image with no problems though (after copying it locally to avoid cross domain network issues), so I'm guessing that Silverlight is ignoring colour profiles or perhaps that it's handling any bad data internally without throwing an exception.
Sunday, August 01, 2010 12:45:14 AM UTC
Aside from ArgumentException, we've encountered COMException, FileFormatException, IOException, NotSupportedException, UnauthorizedAccessException, and (of course) OutOfMemoryException when loading images in WPF; many of these have been due to problems in WPF or the underlying WIC component.

I blogged about the FileFormatException (and its surprising workaround) before (at Image Format Error when loading from a Stream); inspired by this post, I wrote up what we knew about the other exceptions at Exceptions thrown by BitmapImage and BitmapFrame.
Sunday, August 01, 2010 8:38:25 PM UTC
Great stuff - we managed to figure out the ImageFailed workaround for our Twitter client but didn't realize IgnoreColorProfile would do the trick. Thanks!!
James Cadd
Tuesday, March 29, 2011 9:40:56 AM UTC
Awesome!!!

I was dealing with this problem and having none idea how to solve it!!

Thank you very much!

Pau
Comments are closed.

Disclaimer: The opinions expressed herein are my own personal opinions and do not represent my employer's view in any way.