Click here to Skip to main content
15,883,705 members
Articles / Desktop Programming / WPF

EXIF Compare Utility using WPF

Rate me:
Please Sign up or sign in to vote.
5.00/5 (15 votes)
12 Apr 2010CPOL8 min read 68.4K   1.1K   41  
The Exif Compare Utility is a WinDiff equivalent for image files that compares the Exif meta-data and displays the differences and similarities. The application is written using WPF and MVVM.
// <copyright file="ExifValueCreator.cs" company="Nish Sivakumar">
// Copyright (c) Nish Sivakumar. All rights reserved.
// </copyright>

namespace ExifReader
{
    using System;
    using System.Collections.Generic;
    using System.Runtime.InteropServices;

    /// <summary>
    /// A factory class for creating different types of ExifValue objects
    /// </summary>
    internal class ExifValueCreator
    {
        /// <summary>
        /// Delegate map between Exif tag types and associated creation methods
        /// </summary>
        private static Dictionary<PropertyTagType, CreateExifValueDelegate> createExifValueDelegateMap = new Dictionary<PropertyTagType, CreateExifValueDelegate>()
        {
            { PropertyTagType.ASCII, CreateExifValueForASCIIData },
            { PropertyTagType.Byte, CreateExifValueForByteData },
            { PropertyTagType.Short, CreateExifValueForShortData },
            { PropertyTagType.Long, CreateExifValueForLongData },            
            { PropertyTagType.SLong, CreateExifValueForSLongData },            
            { PropertyTagType.Rational, CreateExifValueForRationalData },            
            { PropertyTagType.SRational, CreateExifValueForSRationalData }
        };

        /// <summary>
        /// Delegate that creates the appropriate Exif value of a specific type
        /// </summary>
        /// <param name="value">Array of bytes representing the value or values</param>
        /// <param name="length">Number of values or length of an ASCII string value</param>
        /// <returns>The Exif value or values</returns>
        private delegate IExifValue CreateExifValueDelegate(byte[] value, int length);

        /// <summary>
        /// Creates an ExifValue for a specific type
        /// </summary>
        /// <param name="type">The property data type</param>
        /// <param name="value">An array of bytes representing the value or values</param>
        /// <param name="length">A length parameter specifying the number of values or the length of a string for ASCII string data</param>
        /// <returns>An appropriate IExifValue object</returns>
        internal static IExifValue Create(PropertyTagType type, byte[] value, int length)
        {
            try
            {
                CreateExifValueDelegate createExifValueDelegate;
                if (createExifValueDelegateMap.TryGetValue(type, out createExifValueDelegate))
                {
                    return createExifValueDelegate(value, length);
                }

                return new ExifValue<string>(new[] { type.ToString() });
            }
            catch (Exception ex)
            {
                throw new ExifReaderException(ex);
            }
        }

        /// <summary>
        /// Creates an ExifValue for an undefined value type
        /// </summary>
        /// <param name="tagId">The tag Id whose value needs to be extracted</param>
        /// <param name="value">An array of bytes representing the value or values</param>
        /// <param name="length">The number of bytes</param>
        /// <returns>An appropriate IExifValue object</returns>
        internal static IExifValue CreateUndefined(PropertyTagId tagId, byte[] value, int length)
        {
            var extractor = ExifValueUndefinedExtractorProvider.GetExifValueUndefinedExtractor(tagId);

            try
            {
                return extractor.GetExifValue(value, length);
            }
            catch (Exception ex)
            {
                throw new ExifReaderException(ex, extractor);
            }
        }

        /// <summary>
        /// Creation method for ASCII string values.
        /// </summary>
        /// <param name="value">Bytes representing the string value</param>
        /// <param name="length">Length of the string</param>
        /// <returns>Exif value representing the string</returns>
        private static IExifValue CreateExifValueForASCIIData(byte[] value, int length)
        {
            string[] strings = new string[1]; // There's always 1 string
            System.Text.ASCIIEncoding encoding = new System.Text.ASCIIEncoding();

            strings[0] = encoding.GetString(value).TrimEnd('\0');

            return new ExifValue<string>(strings);
        }

        /// <summary>
        /// Creation method for byte values
        /// </summary>
        /// <param name="value">Bytes representing the byte data</param>
        /// <param name="length">Number of bytes</param>
        /// <returns>Exif value representing the byte data</returns>
        private static IExifValue CreateExifValueForByteData(byte[] value, int length)
        {
            return new ExifValue<byte>(value);
        }

        /// <summary>
        /// Creation method for short values
        /// </summary>
        /// <param name="value">Bytes representing the short data</param>
        /// <param name="length">Number of bytes</param>
        /// <returns>Exif value representing the short data</returns>
        private static IExifValue CreateExifValueForShortData(byte[] value, int length)
        {
            return CreateExifValueForGenericData(value, length, (bytes, pos) => System.BitConverter.ToUInt16(bytes, pos));
        }

        /// <summary>
        /// Creation method for long values
        /// </summary>
        /// <param name="value">Bytes representing the long data</param>
        /// <param name="length">Number of bytes</param>
        /// <returns>Exif value representing the long data</returns>
        private static IExifValue CreateExifValueForLongData(byte[] value, int length)
        {
            return CreateExifValueForGenericData(value, length, (bytes, pos) => System.BitConverter.ToUInt32(bytes, pos));
        }

        /// <summary>
        /// Creation method for signed long values
        /// </summary>
        /// <param name="value">Bytes representing the slong data</param>
        /// <param name="length">Number of bytes</param>
        /// <returns>Exif value representing the slong data</returns>
        private static IExifValue CreateExifValueForSLongData(byte[] value, int length)
        {
            return CreateExifValueForGenericData(value, length, (bytes, pos) => System.BitConverter.ToInt32(bytes, pos));
        }

        /// <summary>
        /// Creation method for Rational values
        /// </summary>
        /// <param name="value">Bytes representing the Rational data</param>
        /// <param name="length">Number of bytes</param>
        /// <returns>Exif value representing the Rational data</returns>
        private static IExifValue CreateExifValueForRationalData(byte[] value, int length)
        {
            return CreateExifValueForGenericData(
                value, 
                length,
                sizeof(uint) * 2,
                (bytes, pos) => new Rational32(System.BitConverter.ToUInt32(bytes, pos), System.BitConverter.ToUInt32(bytes, pos + sizeof(uint))));
        }

        /// <summary>
        /// Creation method for SRational values
        /// </summary>
        /// <param name="value">Bytes representing the SRational data</param>
        /// <param name="length">Number of bytes</param>
        /// <returns>Exif value representing the Rational data</returns>
        private static IExifValue CreateExifValueForSRationalData(byte[] value, int length)
        {
            return CreateExifValueForGenericData(
                value, 
                length,
                sizeof(int) * 2,
                (bytes, pos) => new Rational32(System.BitConverter.ToInt32(bytes, pos), System.BitConverter.ToInt32(bytes, pos + sizeof(int))));
        }

        /// <summary>
        /// Generic creation method
        /// </summary>
        /// <typeparam name="T">The data type of the value data</typeparam>
        /// <param name="value">Bytes representing the data</param>
        /// <param name="length">Number of bytes</param>
        /// <param name="converterFunction">Function that converts from bytes to a specific data type</param>
        /// <returns>Exif value representing the generic data type</returns>
        private static IExifValue CreateExifValueForGenericData<T>(byte[] value, int length, Func<byte[], int, T> converterFunction) where T : struct
        {
            int size = Marshal.SizeOf(typeof(T));
            return CreateExifValueForGenericData(value, length, size, converterFunction);
        }

        /// <summary>
        /// Generic creation method
        /// </summary>
        /// <typeparam name="T">The data type of the value data</typeparam>
        /// <param name="value">Bytes representing the data</param>
        /// <param name="length">Number of bytes</param>
        /// <param name="dataValueSize">Size of each data value</param>
        /// <param name="converterFunction">Function that converts from bytes to a specific data type</param>
        /// <returns>Exif value representing the generic data type</returns>
        private static IExifValue CreateExifValueForGenericData<T>(byte[] value, int length, int dataValueSize, Func<byte[], int, T> converterFunction) where T : struct
        {
            T[] data = new T[length / dataValueSize];

            for (int i = 0, pos = 0; i < length / dataValueSize; i++, pos += dataValueSize)
            {
                data[i] = converterFunction(value, pos);
            }

            return new ExifValue<T>(data);
        }        
    }
}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
United States United States
Nish Nishant is a technology enthusiast from Columbus, Ohio. He has over 20 years of software industry experience in various roles including Chief Technology Officer, Senior Solution Architect, Lead Software Architect, Principal Software Engineer, and Engineering/Architecture Team Leader. Nish is a 14-time recipient of the Microsoft Visual C++ MVP Award.

Nish authored C++/CLI in Action for Manning Publications in 2005, and co-authored Extending MFC Applications with the .NET Framework for Addison Wesley in 2003. In addition, he has over 140 published technology articles on CodeProject.com and another 250+ blog articles on his WordPress blog. Nish is experienced in technology leadership, solution architecture, software architecture, cloud development (AWS and Azure), REST services, software engineering best practices, CI/CD, mentoring, and directing all stages of software development.

Nish's Technology Blog : voidnish.wordpress.com

Comments and Discussions