Click here to Skip to main content
Click here to Skip to main content
Add your own
alternative version
Go to top

Image Manipulation in Multitouch Development

, 3 Apr 2010
In this article, I will describe Image manipulation in Windows 7 multitouch Environment
Win7MultitouchDemo.zip
Win7MultitouchDemo
Win7MultitouchDemo.suo
Win7MultitouchDemo
images
Hydrangeas.jpg
Properties
Settings.settings
Windows7API
MultiTouchAPI
Windows7.Multitouch.WinForms
Properties
Windows7.Multitouch.WinForms.csproj.vspscc
Windows7.Multitouch.WPF
Properties
Windows7.Multitouch.WPF.csproj.vspscc
Windows7.Multitouch
Properties
Windows7.Multitouch.csproj.vspscc
Win7MultitouchDemo_1_.zip
Win7_20MultiTouchAPI_1_.zip
Win7_MultiTouchAPI.zip
MultiTouchAPI
Windows7.Multitouch.WinForms
Properties
Windows7.Multitouch.WinForms.csproj.vspscc
Windows7.Multitouch.WPF
Properties
Windows7.Multitouch.WPF.csproj.vspscc
Windows7.Multitouch
Properties
Windows7.Multitouch.csproj.vspscc
//-----------------------------------------------------------------------------
// Copyright (C) Microsoft Corporation. All rights reserved.
//-----------------------------------------------------------------------------

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Windows7.Multitouch.ManipulationInterop;
using System.Runtime.InteropServices.ComTypes;
using System.Drawing;
using System.Runtime.InteropServices;

namespace Windows7.Multitouch.Manipulation
{
    /// <summary>
    /// Manipulation Flags.
    /// Enables Manipulation Support
    /// </summary>
    [Flags]
    public enum ProcessorManipulations
    {
        /// <summary>
        /// Disable manipulation events
        /// </summary>
        NONE = 0,

        /// <summary>
        /// X axis translation events
        /// </summary>
        TRANSLATE_X = 0x1,

        /// <summary>
        /// Y Axis translation events
        /// </summary>
        TRANSLATE_Y = 0x2,

        /// <summary>
        /// Scaling events
        /// </summary>
        SCALE = 0x4,

        /// <summary>
        /// Rotation events
        /// </summary>
        ROTATE = 0x8,

        /// <summary>
        /// Fire all manipulation events
        /// </summary>
        ALL = 0xf
    }

    /// <summary>
    /// Utility class for vector manipulations
    /// </summary>
    public struct VectorF
    {
        private float _x;
        private float _y;

        /// <summary>
        /// Create new float vector
        /// </summary>
        /// <param name="x">X direction</param>
        /// <param name="y">Y Direction</param>
        public VectorF(float x, float y)
        {
            _x = x;
            _y = y;
        }

        /// <summary>
        /// (x,y)
        /// </summary>
        /// <returns></returns>
        public override string ToString()
        {
            return "(" + _x + ',' + _y + ')';
        }

        /// <summary>
        /// Check if two vectors are equal
        /// </summary>
        /// <param name="obj">the second vector</param>
        /// <returns>true if they are equal</returns>
        public override bool Equals(object obj)
        {
            VectorF other;

            try
            {
                other = (VectorF)obj;
            }
            catch
            {
                return false;
            }

            return obj != null && other._x == _x && other._y == _y;
        }

        /// <summary>
        /// Return the Vector hash code
        /// </summary>
        /// <returns>Hash Code</returns>
        public override int GetHashCode()
        {
            return _x.GetHashCode() ^ _y.GetHashCode();
        }

        /// <summary>
        /// The X direction
        /// </summary>
        public float X
        {
            get { return _x; }
            set { _x = value; }
        }

        /// <summary>
        /// The Y Direction
        /// </summary>
        public float Y
        {
            get { return _y; }
            set { _y = value; }
        }

        /// <summary>
        /// Convert vector to Size
        /// </summary>
        /// <param name="vector">The Vector</param>
        /// <returns>The vector as Size</returns>
        public static implicit operator Size(VectorF vector)
        {
            return new Size((int)vector.X, (int)vector.Y);
        }

        /// <summary>
        /// Convert Vector to SizeF
        /// </summary>
        /// <param name="vector">The Vector</param>
        /// <returns>The vector as SizeF</returns>
        public static implicit operator SizeF(VectorF vector)
        {
            return new SizeF(vector.X, vector.Y);
        }

        /// <summary>
        /// Convert Size to vector
        /// </summary>
        /// <param name="size">The size</param>
        /// <returns>The size as vector</returns>
        public static implicit operator VectorF(Size size)
        {
            return new VectorF(size.Width, size.Height);
        }

        /// <summary>
        /// Convert SizeF to Vector
        /// </summary>
        /// <param name="size">The Size</param>
        /// <returns>The Size as Vector</returns>
        public static implicit operator VectorF(SizeF size)
        {
            return new VectorF(size.Width, size.Height);
        }

        /// <summary>
        /// Multiply the vector with scalar (float)
        /// </summary>
        /// <param name="vector">The source vector</param>
        /// <param name="value">The floating point scalar</param>
        /// <returns>New vector</returns>
        public static VectorF operator*(VectorF vector, float value)
        {
            return new VectorF(vector._x * value, vector._y * value);
        }

        /// <summary>
        /// Divide Vector with scalar (float)
        /// </summary>
        /// <param name="vector">The Vector</param>
        /// <param name="value">The scalar</param>
        /// <returns>New Vector</returns>
        public static VectorF operator /(VectorF vector, float value)
        {
            return vector * (1 / value); 
        }

        /// <summary>
        /// Add scalar to a vector
        /// </summary>
        /// <param name="vector">The Vector</param>
        /// <param name="value">The scalar</param>
        /// <returns>New vector</returns>
        public static VectorF operator +(VectorF vector, float value)
        {
            return new VectorF(vector._x + value, vector._y + value);
        }

        /// <summary>
        /// Substruct Scalar from vector
        /// </summary>
        /// <param name="vector">The vector</param>
        /// <param name="value">The scalar</param>
        /// <returns>New vector</returns>
        public static VectorF operator -(VectorF vector, float value)
        {
            return vector + (-value);
        }

        /// <summary>
        /// Add two vectors
        /// </summary>
        /// <param name="v1">V1</param>
        /// <param name="v2">V2</param>
        /// <returns>New Vector</returns>
        public static VectorF operator +(VectorF v1, VectorF v2)
        {
            return new VectorF(v1._x + v2._x, v1._y + v2._y);
        }

        /// <summary>
        /// Substruct two vectors
        /// </summary>
        /// <param name="v1">V1</param>
        /// <param name="v2">V2</param>
        /// <returns>New Vector</returns>
        public static VectorF operator -(VectorF v1, VectorF v2)
        {
            return new VectorF(v1._x - v2._x, v1._y - v2._y);
        }

        /// <summary>
        /// Return the magnitude (normal) of a vector
        /// </summary>
        public float Magnitude
        {
            get
            {
                return (float)Math.Sqrt(_x * _x + _y * _y);
            }
        }

        /// <summary>
        /// Return the unit vector
        /// </summary>
        public VectorF Direction
        {
            get
            {
                return new VectorF(Math.Sign(_x), Math.Sign(_y));
            }
        }
    }

    /// <summary>
    /// Argument for manipulatiuon start event
    /// </summary>
    public class ManipulationStartedEventArgs : EventArgs
    {
        /// <summary>
        /// Argument for touch event location
        /// </summary>
        /// <param name="x">The x Axis</param>
        /// <param name="y">The y Axis</param>
        public ManipulationStartedEventArgs(float x, float y)
        {
            Location = new PointF(x, y);
        }

        /// <summary>
        /// This location is usually the center point
        /// </summary>
        public PointF Location { get; private set; }
    }

    /// <summary>
    /// The argument for manipulation complete event
    /// </summary>
    public class ManipulationCompletedEventArgs : ManipulationStartedEventArgs
    {
        /// <summary>
        /// Create new ManipulationCompletedEventArgs
        /// </summary>
        /// <param name="x">x Axis</param>
        /// <param name="y">y Axis</param>
        /// <param name="cumulativeTranslationX">Cumulative Translation in the X Axis since starting manipulation</param>
        /// <param name="cumulativeTranslationY">Cumulative Translation in the Y Axis since starting manipulation</param>
        /// <param name="cumulativeScale">Cumulative scaling since starting manipulation</param>
        /// <param name="cumulativeExpansion">Cumulative Expension since starting manipulation</param>
        /// <param name="cumulativeRotation">Cumulative rotation in radians since starting manipulation</param>
        public ManipulationCompletedEventArgs(float x, float y, float cumulativeTranslationX, float cumulativeTranslationY,
                                                    float cumulativeScale, float cumulativeExpansion, float cumulativeRotation)
            : base(x, y)
        {
            CumulativeTranslation = new SizeF(cumulativeTranslationX, cumulativeTranslationY);
            CumulativeScale = cumulativeScale;
            CumulativeExpansion = cumulativeExpansion;
            CumulativeRotation = cumulativeRotation;
        }

        /// <summary>
        /// Total translation
        /// </summary>
        public SizeF CumulativeTranslation { get; private set; }

        /// <summary>
        /// Total Scaling
        /// </summary>
        public float CumulativeScale { get; private set; }

        /// <summary>
        /// Total Extension
        /// </summary>
        public float CumulativeExpansion { get; private set; }

        /// <summary>
        /// Total Rotation
        /// </summary>
        public float CumulativeRotation { get; private set; }
    }

    /// <summary>
    /// The argument for manipulation delta
    /// </summary>
    public class ManipulationDeltaEventArgs : ManipulationCompletedEventArgs
    {
        /// <summary>
        /// 
        /// </summary>
        /// <param name="x">x Axis</param>
        /// <param name="y">y Axis</param>
        /// <param name="translationDeltaX">The amount of translation since the last event</param>
        /// <param name="translationDeltaY">The amount of translation since the last event</param>
        /// <param name="scaleDelta">The amount of scaling since the last event</param>
        /// <param name="expansionDelta">The amount of expension since the last event</param>
        /// <param name="rotationDelta">The amount of rotation in radians since the last event</param>
        /// <param name="cumulativeTranslationX">Cumulative Translation in the X Axis since starting manipulation</param>
        /// <param name="cumulativeTranslationY">Cumulative Translation in the Y Axis since starting manipulation</param>
        /// <param name="cumulativeScale">Cumulative scaling since starting manipulation</param>
        /// <param name="cumulativeExpansion">Cumulative Expension since starting manipulation</param>
        /// <param name="cumulativeRotation">Cumulative rotation in radians since starting manipulation</param>
        public ManipulationDeltaEventArgs(float x, float y, float translationDeltaX, float translationDeltaY, float scaleDelta, float expansionDelta,
            float rotationDelta, float cumulativeTranslationX, float cumulativeTranslationY, float cumulativeScale, float cumulativeExpansion, float cumulativeRotation)
            : base(x, y, cumulativeTranslationX, cumulativeTranslationY, cumulativeScale, cumulativeExpansion, cumulativeRotation)
        {
            TranslationDelta = new SizeF(translationDeltaX, translationDeltaY);
            ScaleDelta = scaleDelta;
            ExpansionDelta = expansionDelta;
            RotationDelta = rotationDelta;

        }

        /// <summary>
        /// The amount of translation since the last event
        /// </summary>
        public SizeF TranslationDelta { get; private set; }

        /// <summary>
        /// The amount of scaling since the last event
        /// </summary>
        public float ScaleDelta { get; private set; }
        
        /// <summary>
        /// The amount of expension since the last event
        /// </summary>
        public float ExpansionDelta { get; private set; }

        /// <summary>
        /// The amount of rotation since the last event
        /// </summary>
        public float RotationDelta { get; private set; }

    }

    /// <summary>
    /// A .NET wrapper for touch manipulation processing
    /// </summary>
    public class ManipulationProcessor : IManipulationEvents, IDisposable
    {

        private readonly IManipulationProcessor _comManipulationProcessor;
        private readonly IManipulationEvents _comManipulationEvents;

        /// <summary>
        /// Create new manipulation processor
        /// </summary>
        /// <remarks>
        /// Call the <see cref="ProcessDown"/>, <see cref="ProcessMove"/>, <see cref="ProcessUp"/> to feed the processor.
        /// Register on <see cref="ManipulationStarted"/>, <see cref="ManipulationDelta"/> and <see cref="ManipulationCompleted"/>
        /// to handle manipulation events
        /// </remarks>
        /// <param name="supportedManipulations">Activate specific manipulation (scale, translate, rotate)</param>
        public ManipulationProcessor(ProcessorManipulations supportedManipulations)
        {
            _comManipulationProcessor = new ManipulationInterop.ManipulationProcessor();
            _comManipulationEvents = new ManipulationEvents(_comManipulationProcessor, ManipulationEventHandler);

            SupportedManipulations = supportedManipulations;
        }

        internal virtual IManipulationEvents ManipulationEventHandler
        {
            get
            {
                return this;
            }
        }
                
        /// <summary>
        /// Fired when manipulation is started
        /// </summary>
        public event EventHandler<ManipulationStartedEventArgs> ManipulationStarted = (s, e) => { };
        
        /// <summary>
        /// Fired each time the processor had figured a change in one or more of the required manipulations
        /// </summary>
        public event EventHandler<ManipulationDeltaEventArgs> ManipulationDelta = (s, e) => { };

        /// <summary>
        /// Fired on manipulation end
        /// </summary>
        public event EventHandler<ManipulationCompletedEventArgs> ManipulationCompleted = (s, e) => { };

        #region IManipulationEvents Members
        void IManipulationEvents.ManipulationStarted(float x, float y)
        {
            ManipulationStarted(this, new ManipulationStartedEventArgs(x, y));
        }

        void IManipulationEvents.ManipulationDelta(float x, float y, float translationDeltaX, float translationDeltaY, float scaleDelta, float expansionDelta, float rotationDelta,
            float cumulativeTranslationX, float cumulativeTranslationY, float cumulativeScale, float cumulativeExpansion, float cumulativeRotation)
        {
            ManipulationDelta(this, new ManipulationDeltaEventArgs(x, y, translationDeltaX, translationDeltaY, scaleDelta, expansionDelta, rotationDelta,
                cumulativeTranslationX, cumulativeTranslationY, cumulativeScale, cumulativeExpansion, cumulativeRotation));
        }

        void IManipulationEvents.ManipulationCompleted(float x, float y, float cumulativeTranslationX, float cumulativeTranslationY, float cumulativeScale, float cumulativeExpansion,
            float cumulativeRotation)
        {
            ManipulationCompleted(this, new ManipulationCompletedEventArgs(x, y, cumulativeTranslationX, cumulativeTranslationY, cumulativeScale,
                cumulativeExpansion, cumulativeRotation));
        }

        #endregion

        /// <summary>
        /// Get or Set the required manipulation
        /// </summary>
        public ProcessorManipulations SupportedManipulations
        {
            get
            {
                return (ProcessorManipulations)_comManipulationProcessor.SupportedManipulations;
            }
            set
            {
                _comManipulationProcessor.SupportedManipulations = (MANIPULATION_PROCESSOR_MANIPULATIONS)value;
            }
        }

        /// <summary>
        /// The Center of the object
        /// </summary>
        public PointF PivotPoint
        {
            get
            {
                return new PointF(_comManipulationProcessor.PivotPointX, _comManipulationProcessor.PivotPointY);
            }
            set
            {
                _comManipulationProcessor.PivotPointX = value.X;
                _comManipulationProcessor.PivotPointY = value.Y;
            }
        }

        /// <summary>
        /// The PivotRadius property is used to determine how much rotation is used in single finger manipulation
        /// </summary>
        public float PivotRadius
        {
            get { return _comManipulationProcessor.PivotRadius; }
            set { _comManipulationProcessor.PivotRadius = value; }
        }

        /// <summary>
        /// This method raises the ManipulationCompleted() event in response
        /// </summary>
        public void CompleteManipulation()
        {
            _comManipulationProcessor.CompleteManipulation();
        }

        /// <summary>
        /// The ProcessDown method feeds data to the manipulation processor associated with a target
        /// </summary>
        /// <param name="manipulationId">The identifier for the manipulation that you want to process</param>
        /// <param name="location">The coordinates associated with the target</param>
        public void ProcessDown(uint manipulationId, PointF location)
        {
            _comManipulationProcessor.ProcessDown(manipulationId, location.X, location.Y);
        }

        /// <summary>
        /// The ProcessMove method feeds movement data for the target object to its manipulation processor
        /// </summary>
        /// <param name="manipulationId">The identifier for the manipulation that you want to process</param>
        /// <param name="location">The coordinates associated with the target</param>
        public void ProcessMove(uint manipulationId, PointF location)
        {
            _comManipulationProcessor.ProcessMove(manipulationId, location.X, location.Y);
        }

        /// <summary>
        /// The ProcessUp method feeds data to a target's manipulation processor for touch up sequences
        /// </summary>
        /// <param name="manipulationId">The identifier for the manipulation that you want to process</param>
        /// <param name="location">The coordinates associated with the target</param>
        public void ProcessUp(uint manipulationId, PointF location)
        {
            _comManipulationProcessor.ProcessUp(manipulationId, location.X, location.Y);
        }

        /// <summary>
        /// Feds data to the manipulation processor associated with a target and a timestamp
        /// </summary>
        /// <param name="manipulationId">The identifier for the manipulation that you want to process</param>
        /// <param name="location">The coordinates associated with the target</param>
        /// <param name="timestamp">The timestamp of the event</param>
        public void ProcessDownWithTime(uint manipulationId, PointF location, int timestamp)
        {
            _comManipulationProcessor.ProcessDownWithTime(manipulationId, location.X, location.Y, timestamp);
        }

        /// <summary>
        /// Feds data to the manipulation processor associated with a target and a timestamp
        /// </summary>
        /// <param name="manipulationId">The identifier for the manipulation that you want to process</param>
        /// <param name="location">The coordinates associated with the target</param>
        /// <param name="timestamp">The timestamp of the event</param>
        public void ProcessMoveWithTime(uint manipulationId, PointF location, int timestamp)
        {
            _comManipulationProcessor.ProcessMoveWithTime(manipulationId, location.X, location.Y, timestamp);
        }

        /// <summary>
        /// Feds data to the manipulation processor associated with a target and a timestamp
        /// </summary>
        /// <param name="manipulationId">The identifier for the manipulation that you want to process</param>
        /// <param name="location">The coordinates associated with the target</param>
        /// <param name="timestamp">The timestamp of the event</param>
        public void ProcessUpWithTime(uint manipulationId, PointF location, int timestamp)
        {
            _comManipulationProcessor.ProcessUpWithTime(manipulationId, location.X, location.Y, timestamp);
        }

        /// <summary>
        /// Calculates and returns the velocity for the target object
        /// </summary>
        public VectorF Velocity
        {
            get
            {
                return new VectorF(_comManipulationProcessor.GetVelocityX(), _comManipulationProcessor.GetVelocityY());
            }
        }

        /// <summary>
        /// Calculates the rate that the target object is expanding at
        /// </summary>
        public float ExpansionVelocity
        {
            get
            {
                return _comManipulationProcessor.GetExpansionVelocity();
            }
        }

        /// <summary>
        /// Calculates the rotational velocity that the target object is moving at
        /// </summary>
        public float AngularVelocity
        {
            get
            {
                return _comManipulationProcessor.GetAngularVelocity();
            }
        }

        /// <summary>
        /// Specifies the minimum scale and rotate radius
        /// </summary>
        public float MinimumScaleRotateRadius
        {
            get
            {
                return _comManipulationProcessor.MinimumScaleRotateRadius;
            }
            set
            {
                _comManipulationProcessor.MinimumScaleRotateRadius = value;
            }
        }

        /// <summary>
        /// Release the underlined COM Object
        /// </summary>
        /// <param name="dispose"></param>
        protected virtual void Dispose(bool dispose)
        {
            if (dispose)
            {
                GC.SuppressFinalize(this);
                Marshal.ReleaseComObject(_comManipulationProcessor);
                _comManipulationEvents.Dispose();
            }
        }

        #region IDisposable Members

        /// <summary>
        /// Dispose the object, free the underline COM object
        /// </summary>
        public void Dispose()
        {
            Dispose(true);
        }

        #endregion
    }
}

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)

Share

About the Author

_ Kunal Chowdhury _
Technical Lead
India India
Kunal Chowdhury is a Microsoft "Client Development" MVP (Most Valuable Professional), a Codeproject Mentor, Telerik MVP, Nokia Developer Champion, Speaker in various Microsoft events, Author, passionate Blogger and a Software Engineer by profession.
 
He is currently working in an MNC located in India. He has a very good skill over XAML, C#, Silverlight, Windows Phone, WPF and Windows 8 (WinRT). He posts his findings, articles in his technical blog and CodeProject.
 
Technical Blog: http://www.kunal-chowdhury.com
Facebook: http://facebook.com/blog.kunal
Twitter : http://twitter.com/kunal2383
Follow on   Twitter   Google+   LinkedIn

| Advertise | Privacy | Mobile
Web04 | 2.8.140916.1 | Last Updated 3 Apr 2010
Article Copyright 2010 by _ Kunal Chowdhury _
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid