VintaSoft Imaging .NET SDK 15.1: Documentation for .NET developer
In This Topic
How to create custom mark annotation?
In This Topic
VintaSoft Annotation .NET Plug-in allows to create custom annotation from scratch, i.e.: Also Plug-in allows to use any standard annotation as a base class for custom annotation because SDK provides open architecture.


This article describes how to create mark annotation from scratch. For doing the task is necessary to create 1 enumeration and 4 classes:

MarkAnnotationType enumeration

MarkAnnotationType enumeration defines possible mark type: rectangle, tick, star or cross.

Here is C#/VB.NET code that shows implementation of MarkAnnotationType enumeration:
namespace CommonCode.Annotation
{
    /// <summary>
    /// Specifies the available types of mark annotation.
    /// </summary>
    public enum MarkAnnotationType : int
    {
        /// <summary>
        /// The rectangle.
        /// </summary>
        Rectangle = 0,

        /// <summary>
        /// The tick.
        /// </summary>
        Tick = 1,

        /// <summary>
        /// The star.
        /// </summary>
        Star = 2,

        /// <summary>
        /// The cross.
        /// </summary>
        Cross = 3,
    }
}

Namespace CommonCode.Annotation
    ''' <summary>
    ''' Specifies the available types of mark annotation.
    ''' </summary>
    Public Enum MarkAnnotationType As Integer
        ''' <summary>
        ''' The rectangle.
        ''' </summary>
        Rectangle = 0

        ''' <summary>
        ''' The tick.
        ''' </summary>
        Tick = 1

        ''' <summary>
        ''' The star.
        ''' </summary>
        Star = 2

        ''' <summary>
        ''' The cross.
        ''' </summary>
        Cross = 3
    End Enum
End Namespace



MarkAnnotationData class

MarkAnnotationData class:
Here is C#/VB.NET code that shows implementation of MarkAnnotationData class:
using System;
using System.ComponentModel;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Runtime.Serialization;
using System.Security.Permissions;

using Vintasoft.Imaging;
using Vintasoft.Imaging.Annotation;
using Vintasoft.Imaging.Annotation.Rendering;

namespace CommonCode.Annotation
{
    /// <summary>
    /// Class that holds information about the annotation that displays a mark.
    /// </summary>
    [Serializable]
    public class MarkAnnotationData : AnnotationData
    {

        #region Constructors

        /// <summary>
        /// Initializes a new instance of the <see cref="MarkAnnotationData"/> class.
        /// </summary>
        public MarkAnnotationData()
            : base()
        {
            FillBrush = new AnnotationSolidBrush(Color.Black);
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="AnnotationData"/> class.
        /// </summary>
        /// <param name="info">The SerializationInfo to populate with data.</param>
        /// <param name="context">The destination for this serialization.</param>
        public MarkAnnotationData(SerializationInfo info, StreamingContext context)
            : base(info, context)
        {
            _markType = (MarkAnnotationType)info.GetValue("MarkType", typeof(int));
        }


        /// <summary>
        /// Initializes the <see cref="MarkAnnotationData"/> class.
        /// </summary>
        static MarkAnnotationData()
        {
            // register renderer form this annotation
            AnnotationRendererFactory.RegisterRendererForAnnotationData(typeof(MarkAnnotationData), typeof(MarkAnnotationRenderer));
        }

        #endregion



        #region Properties

        MarkAnnotationType _markType = MarkAnnotationType.Tick;
        /// <summary>
        /// Gets or sets a mark type.
        /// </summary>
        [Description("The mark type.")]
        [DefaultValue(MarkAnnotationType.Tick)]
        public MarkAnnotationType MarkType
        {
            get
            {
                return _markType;
            }
            set
            {
                if (_markType != value)
                {
                    ObjectPropertyChangingEventArgs changingArgs =
                        new ObjectPropertyChangingEventArgs("MarkType", _markType, value);
                    if (OnPropertyChanging(changingArgs))
                    {
                        _markType = (MarkAnnotationType)changingArgs.NewValue;
                        OnPropertyChanged(changingArgs.ToChangedEventArgs());
                    }
                }
            }
        }

        #endregion



        #region Methods

        /// <summary>
        /// Returns the bounding box of annotation if annotation will have specified location,
        /// size and rotation.
        /// </summary>
        /// <param name="location">Location, in device-independent pixels (1/96th inch),
        /// of annotation.</param>
        /// <param name="size">Size, in device-independent pixels (1/96th inch),
        /// of annotation</param>
        /// <param name="rotation">Rotation, in degrees, of annotation.</param>
        /// <returns>Bounding box of annotation.</returns>
        public override RectangleF GetBoundingBox(PointF location, SizeF size, float rotation)
        {
            PointF[] points = GetReferencePointsInContentSpace();

            // rotate
            AnnotationsMath.RotatePointsAt(points, PointF.Empty, Rotation);
            // scale
            AnnotationsMath.ScalePoints(points, size.Width / this.Size.Width, size.Height / this.Size.Height);
            // translate
            AnnotationsMath.TranslatePoints(points, location.X, location.Y);

            return AnnotationsMath.GetBoundingBox(points);
        }


        /// <summary>
        /// Gets an array that contains reference points in content space of this annotation.
        /// </summary>
        /// <returns></returns>
        public virtual PointF[] GetReferencePointsInContentSpace()
        {
            float width = Size.Width;
            float height = Size.Height;
            float w = Math.Min(width / 10, height / 10);

            PointF[] points;
            switch (MarkType)
            {
                case MarkAnnotationType.Rectangle:
                    points = new PointF[]{
                        new PointF(-width / 2, -height/2),
                        new PointF(width / 2, -height/2),
                        new PointF(width / 2, height/2),
                        new PointF(-width / 2, height/2)};
                    break;

                case MarkAnnotationType.Tick:
                    points = new PointF[]{
                        new PointF(-width / 2, 0),
                        new PointF(0, height / 4),
                        new PointF(width / 2, -height / 2),
                        new PointF(0, height / 2),
                        new PointF(-width / 2, 0)};
                    break;

                case MarkAnnotationType.Cross:
                    points = new PointF[]{
                        new PointF(-width / 2, -w),
                        new PointF(-w, -w),
                        new PointF(-w, -height/2),
                        new PointF(w, -height/2),
                        new PointF(w, -w),
                        new PointF(width/2, -w),
                        new PointF(width/2, w),
                        new PointF(w, w),
                        new PointF(w, height/2),
                        new PointF(-w, height/2),
                        new PointF(-w, w),
                        new PointF(-width/2, w)};
                    break;

                case MarkAnnotationType.Star:
                    points = new PointF[]{
                        new PointF(-width / 2, 0),
                        new PointF(-w, -w),
                        new PointF(0, -height/2),
                        new PointF(w, -w),
                        new PointF(width/2, 0),
                        new PointF(w, w),
                        new PointF(0, height/2),
                        new PointF(-w, w),
                        new PointF(-width/2, 0)};
                    break;

                default:
                    throw new NotImplementedException();
            }

            AffineMatrix matrix = new AffineMatrix();
            if (HorizontalMirrored)
                matrix.ScalePrepend(-1, 1);
            if (VerticalMirrored)
                matrix.ScalePrepend(1, -1);

            PointFAffineTransform.TransformPoints(matrix, points);

            return points;
        }


        /// <summary>
        /// Populates a SerializationInfo with the data needed to serialize the target object.
        /// </summary>
        /// <param name="info">The SerializationInfo to populate with data.</param>
        /// <param name="context">The destination for this serialization.</param>
        [SecurityPermission(SecurityAction.LinkDemand, Flags = SecurityPermissionFlag.SerializationFormatter)]
        public override void GetObjectData(SerializationInfo info, StreamingContext context)
        {
            base.GetObjectData(info, context);
            info.AddValue("MarkType", (int)MarkType);
        }

        
        /// <summary>
        /// Creates a new object that is a copy of the current instance.
        /// </summary>
        /// <returns>A new object that is a copy of this instance.</returns>
        public override object Clone()
        {
            MarkAnnotationData data = new MarkAnnotationData();
            CopyTo(data);
            return data;
        }

        /// <summary>
        /// Copies the state of the current object to the target object.
        /// </summary>
        /// <param name="target">Object to copy the state of the current object to.</param>
        public override void CopyTo(AnnotationData target)
        {
            base.CopyTo(target);

            if (target is MarkAnnotationData)
            {
                ((MarkAnnotationData)target).MarkType = MarkType;
            }
        }

        #endregion

    }
}

Imports System.ComponentModel
Imports System.Drawing
Imports System.Drawing.Drawing2D
Imports System.Runtime.Serialization
Imports System.Security.Permissions

Imports Vintasoft.Imaging
Imports Vintasoft.Imaging.Annotation
Imports Vintasoft.Imaging.Annotation.Rendering

Namespace CommonCode.Annotation
    ''' <summary>
    ''' Class that holds information about the annotation that displays a mark.
    ''' </summary>
    <Serializable> _
    Public Class MarkAnnotationData
        Inherits AnnotationData

        #Region "Constructors"

        ''' <summary>
        ''' Initializes a new instance of the <see cref="MarkAnnotationData"/> class.
        ''' </summary>
        Public Sub New()
            MyBase.New()
            FillBrush = New AnnotationSolidBrush(Color.Black)
        End Sub

        ''' <summary>
        ''' Initializes a new instance of the <see cref="AnnotationData"/> class.
        ''' </summary>
        ''' <param name="info">The SerializationInfo to populate with data.</param>
        ''' <param name="context">The destination for this serialization.</param>
        Public Sub New(info As SerializationInfo, context As StreamingContext)
            MyBase.New(info, context)
            _markType = CType(info.GetValue("MarkType", GetType(Integer)), MarkAnnotationType)
        End Sub


        ''' <summary>
        ''' Initializes the <see cref="MarkAnnotationData"/> class.
        ''' </summary>
        Shared Sub New()
            ' register renderer form this annotation
            AnnotationRendererFactory.RegisterRendererForAnnotationData(GetType(MarkAnnotationData), GetType(MarkAnnotationRenderer))
        End Sub

        #End Region



        #Region "Properties"

        Private _markType As MarkAnnotationType = MarkAnnotationType.Tick
        ''' <summary>
        ''' Gets or sets a mark type.
        ''' </summary>
        <Description("The mark type.")> _
        <DefaultValue(MarkAnnotationType.Tick)> _
        Public Property MarkType() As MarkAnnotationType
            Get
                Return _markType
            End Get
            Set
                If _markType <> value Then
                    Dim changingArgs As New ObjectPropertyChangingEventArgs("MarkType", _markType, value)
                    If OnPropertyChanging(changingArgs) Then
                        _markType = CType(changingArgs.NewValue, MarkAnnotationType)
                        OnPropertyChanged(changingArgs.ToChangedEventArgs())
                    End If
                End If
            End Set
        End Property

        #End Region



        #Region "Methods"

        ''' <summary>
        ''' Returns the bounding box of annotation if annotation will have specified location,
        ''' size and rotation.
        ''' </summary>
        ''' <param name="location">Location, in device-independent pixels (1/96th inch),
        ''' of annotation.</param>
        ''' <param name="size">Size, in device-independent pixels (1/96th inch),
        ''' of annotation</param>
        ''' <param name="rotation">Rotation, in degrees, of annotation.</param>
        ''' <returns>Bounding box of annotation.</returns>
        Public Overrides Function GetBoundingBox(location As PointF, size As SizeF, rotation__1 As Single) As RectangleF
            Dim points As PointF() = GetReferencePointsInContentSpace()

            ' rotate
            AnnotationsMath.RotatePointsAt(points, PointF.Empty, Rotation)
            ' scale
            AnnotationsMath.ScalePoints(points, size.Width / Me.Size.Width, size.Height / Me.Size.Height)
            ' translate
            AnnotationsMath.TranslatePoints(points, location.X, location.Y)

            Return AnnotationsMath.GetBoundingBox(points)
        End Function


        ''' <summary>
        ''' Gets an array that contains reference points in content space of this annotation.
        ''' </summary>
        ''' <returns></returns>
        Public Overridable Function GetReferencePointsInContentSpace() As PointF()
            Dim width As Single = Size.Width
            Dim height As Single = Size.Height
            Dim w As Single = Math.Min(width / 10, height / 10)

            Dim points As PointF()
            Select Case MarkType
                Case MarkAnnotationType.Rectangle
                    points = New PointF() {New PointF(-width / 2, -height / 2), New PointF(width / 2, -height / 2), New PointF(width / 2, height / 2), New PointF(-width / 2, height / 2)}
                    Exit Select

                Case MarkAnnotationType.Tick
                    points = New PointF() {New PointF(-width / 2, 0), New PointF(0, height / 4), New PointF(width / 2, -height / 2), New PointF(0, height / 2), New PointF(-width / 2, 0)}
                    Exit Select

                Case MarkAnnotationType.Cross
                    points = New PointF() {New PointF(-width / 2, -w), New PointF(-w, -w), New PointF(-w, -height / 2), New PointF(w, -height / 2), New PointF(w, -w), New PointF(width / 2, -w), _
                        New PointF(width / 2, w), New PointF(w, w), New PointF(w, height / 2), New PointF(-w, height / 2), New PointF(-w, w), New PointF(-width / 2, w)}
                    Exit Select

                Case MarkAnnotationType.Star
                    points = New PointF() {New PointF(-width / 2, 0), New PointF(-w, -w), New PointF(0, -height / 2), New PointF(w, -w), New PointF(width / 2, 0), New PointF(w, w), _
                        New PointF(0, height / 2), New PointF(-w, w), New PointF(-width / 2, 0)}
                    Exit Select
                Case Else

                    Throw New NotImplementedException()
            End Select

            Dim matrix As New AffineMatrix()
            If HorizontalMirrored Then
                matrix.ScalePrepend(-1, 1)
            End If
            If VerticalMirrored Then
                matrix.ScalePrepend(1, -1)
            End If

            PointFAffineTransform.TransformPoints(matrix, points)

            Return points
        End Function


        ''' <summary>
        ''' Populates a SerializationInfo with the data needed to serialize the target object.
        ''' </summary>
        ''' <param name="info">The SerializationInfo to populate with data.</param>
        ''' <param name="context">The destination for this serialization.</param>
        <SecurityPermission(SecurityAction.LinkDemand, Flags := SecurityPermissionFlag.SerializationFormatter)> _
        Public Overrides Sub GetObjectData(info As SerializationInfo, context As StreamingContext)
            MyBase.GetObjectData(info, context)
            info.AddValue("MarkType", CInt(MarkType))
        End Sub


        ''' <summary>
        ''' Creates a new object that is a copy of the current instance.
        ''' </summary>
        ''' <returns>A new object that is a copy of this instance.</returns>
        Public Overrides Function Clone() As Object
            Dim data As New MarkAnnotationData()
            CopyTo(data)
            Return data
        End Function

        ''' <summary>
        ''' Copies the state of the current object to the target object.
        ''' </summary>
        ''' <param name="target">Object to copy the state of the current object to.</param>
        Public Overrides Sub CopyTo(target As AnnotationData)
            MyBase.CopyTo(target)

            If TypeOf target Is MarkAnnotationData Then
                DirectCast(target, MarkAnnotationData).MarkType = MarkType
            End If
        End Sub

        #End Region

    End Class
End Namespace



MarkAnnotationBuilder class

MarkAnnotationBuilder class performs visual building of annotation.
MarkAnnotationBuilder class:
Here is C#/VB.NET code that shows implementation of MarkAnnotationBuilder class:
using System.Drawing;

using Vintasoft.Imaging.UI.VisualTools.UserInteraction;

namespace CommonCode.Annotation
{
    /// <summary>
    /// Interaction controller that builds a mark annotation.
    /// </summary>
    public class MarkAnnotationBuilder : InteractionControllerBase<IRectangularInteractiveObject>
    {

        #region Contructors

        /// <summary>
        /// Initializes a new instance of the <see cref="MarkAnnotationBuilder"/> class.
        /// </summary>
        /// <param name="view">The mark annotation.</param>
        public MarkAnnotationBuilder(MarkAnnotationView view)
            : base(view)
        {
            // create an interaction area that can be moved, hovered and clicked
            ImageViewerArea buildArea = new ImageViewerArea(
                InteractionAreaType.Hover | InteractionAreaType.Movable | InteractionAreaType.Clickable);
            // mark that any mouse button can interact with interaction area
            buildArea.AnyActionMouseButton = true;
            // add the interacton area to a list of interaction areas of the interaction controller
            InteractionAreaList.Add(buildArea);

            // set initial size of annotation
            _initialSize = view.Size;
        }

        #endregion



        #region Properties
        
        SizeF _initialSize;
        /// <summary>
        /// Gets or sets an annotation initial size.
        /// </summary>
        public SizeF InitialSize
        {
            get
            {
                return _initialSize;
            }
            set
            {
                _initialSize = value;
            }
        }

        #endregion



        #region Methods

        /// <summary>
        /// Performs an interaction between user and interaction area.
        /// </summary>
        /// <param name="args">An interaction event args.</param>
        protected override void PerformInteraction(InteractionEventArgs args)
        {
            // set rectangle of annotation
            InteractiveObject.SetRectangle(
                        args.Location.X - _initialSize.Width / 2, args.Location.Y - _initialSize.Height / 2,
                        args.Location.X + _initialSize.Width / 2, args.Location.Y + _initialSize.Height / 2);

            // mark that the user interacted with annotation
            args.InteractionOccured(true);

            // if any mouse button is clicked
            if (args.Action == InteractionAreaAction.Click)
                // finish building of annotation
                args.InteractionFinished = true;
        }

        #endregion

    }
}

Imports System.Drawing

Imports Vintasoft.Imaging.UI.VisualTools.UserInteraction

Namespace CommonCode.Annotation
    ''' <summary>
    ''' Interaction controller that builds a mark annotation.
    ''' </summary>
    Public Class MarkAnnotationBuilder
        Inherits InteractionControllerBase(Of IRectangularInteractiveObject)

        #Region "Contructors"

        ''' <summary>
        ''' Initializes a new instance of the <see cref="MarkAnnotationBuilder"/> class.
        ''' </summary>
        ''' <param name="view">The mark annotation.</param>
        Public Sub New(view As MarkAnnotationView)
            MyBase.New(view)
            ' create an interaction area that can be moved, hovered and clicked
            Dim buildArea As New ImageViewerArea(InteractionAreaType.Hover Or InteractionAreaType.Movable Or InteractionAreaType.Clickable)
            ' mark that any mouse button can interact with interaction area
            buildArea.AnyActionMouseButton = True
            ' add the interacton area to a list of interaction areas of the interaction controller
            InteractionAreaList.Add(buildArea)

            ' set initial size of annotation
            _initialSize = view.Size
        End Sub

        #End Region



        #Region "Properties"

        Private _initialSize As SizeF
        ''' <summary>
        ''' Gets or sets an annotation initial size.
        ''' </summary>
        Public Property InitialSize() As SizeF
            Get
                Return _initialSize
            End Get
            Set
                _initialSize = value
            End Set
        End Property

        #End Region



        #Region "Methods"

        ''' <summary>
        ''' Performs an interaction between user and interaction area.
        ''' </summary>
        ''' <param name="args">An interaction event args.</param>
        Protected Overrides Sub PerformInteraction(args As InteractionEventArgs)
            ' set rectangle of annotation
            InteractiveObject.SetRectangle(args.Location.X - _initialSize.Width / 2, args.Location.Y - _initialSize.Height / 2, args.Location.X + _initialSize.Width / 2, args.Location.Y + _initialSize.Height / 2)

            ' mark that the user interacted with annotation
            args.InteractionOccured(True)

            ' if any mouse button is clicked
            If args.Action = InteractionAreaAction.Click Then
                ' finish building of annotation
                args.InteractionFinished = True
            End If
        End Sub

        #End Region

    End Class
End Namespace



MarkAnnotationView class

MarkAnnotationView class:
Here is C#/VB.NET code that shows implementation of MarkAnnotationView class:
using System;
using System.ComponentModel;
using System.Drawing;
using System.Drawing.Drawing2D;

using Vintasoft.Imaging;
using Vintasoft.Imaging.Annotation;
using Vintasoft.Imaging.Annotation.UI;
using Vintasoft.Imaging.Annotation.UI.VisualTools.UserInteraction;
using Vintasoft.Imaging.Drawing;
using Vintasoft.Imaging.UI.VisualTools.UserInteraction;

namespace CommonCode.Annotation
{
    /// <summary>
    /// Class that determines how to display the annotation that displays a mark
    /// and how user can interact with annotation.
    /// </summary>
    public class MarkAnnotationView : AnnotationView, IRectangularInteractiveObject
    {

        #region Constructors

        /// <summary>
        /// Initializes a new instance of the <see cref="MarkAnnotationView"/> class.
        /// </summary>
        /// <param name="annotationData">Object that stores the annotation data.</param>
        public MarkAnnotationView(MarkAnnotationData annotationData)
            : base(annotationData)
        {
            SizeF initialSize = Size;
            if (initialSize.IsEmpty)
            {
                initialSize = new Size(64, 64);
                Size = initialSize;
            }
            Builder = new MarkAnnotationBuilder(this);

            RectangularAnnotationTransformer transformer = new RectangularAnnotationTransformer(this);
            transformer.HideInteractionPointsWhenMoving = true;
            foreach (InteractionPoint point in transformer.ResizePoints)
                point.FillColor = Color.FromArgb(100, Color.Red);
            Transformer = transformer;
        }

        #endregion



        #region Properties

        /// <summary>
        /// Gets or sets a mark type.
        /// </summary>
        [Description("The mark type.")]
        [DefaultValue(MarkAnnotationType.Tick)]
        public MarkAnnotationType MarkType
        {
            get
            { 
                return MarkAnnoData.MarkType;
            }
            set 
            { 
                MarkAnnoData.MarkType = value;
            }
        }

        /// <summary>
        /// Gets an annotation data.
        /// </summary>
        MarkAnnotationData MarkAnnoData
        {
            get 
            { 
                return (MarkAnnotationData)Data;
            }
        }

        /// <summary>
        /// Gets or sets the rotation angle of interactive object.
        /// </summary>
        double IRectangularInteractiveObject.RotationAngle
        {
            get 
            { 
                return Rotation; 
            }
            set 
            { 
                Rotation = (float)value;
            }
        }

        #endregion



        #region Methods

        #region PUBLIC

        /// <summary>
        /// Indicates whether the specified point is contained within the annotation.
        /// </summary>
        /// <param name="point">Point in image space.</param>
        /// <param name="ignoreContainmentCheckDistance">A value indicating whether value of <see cref="AnnotationView.ContainmentCheckDistance"/> property must be ignored.</param>
        /// <returns><b>true</b> if the specified point is contained within the annotation;
        /// otherwise, <b>false</b>.</returns>
        public override bool IsPointOnFigure(PointF point, bool ignoreContainmentCheckDistance)
        {
            using (IGraphicsPath path = ((MarkAnnotationRenderer)Renderer).GetAsGraphicsPath(DrawingFactory.Default))
            {
                path.Transform(GetTransformFromContentToImageSpace());
                using (IDrawingPen pen = DrawingFactory.Default.CreatePen(Outline))
                {
                    return path.Contains(point) || path.OutlineContains(point, pen);
                }
            }
        }

        /// <summary>
        /// Creates a new object that is a copy of the current
        /// <see cref="MarkAnnotationView"/> instance.
        /// </summary>
        /// <returns>A new object that is a copy of this <see cref="MarkAnnotationView"/>
        /// instance.</returns>
        public override object Clone()
        {
            return new MarkAnnotationView((MarkAnnotationData)this.Data.Clone());
        }

        /// <summary>
        /// Returns an annotation selection as <see cref="GraphicsPath"/> in annotation content space.
        /// </summary>
        public override GraphicsPath GetSelectionAsGraphicsPath()
        {
            GraphicsPath path = new GraphicsPath();
            SizeF size = Size;
            path.AddRectangle(new RectangleF(-size.Width / 2, -size.Height / 2, size.Width, size.Height));
            using (Matrix transform = GdiConverter.Convert(GetTransformFromContentToImageSpace()))
                path.Transform(transform);
            return path;
        }

        #endregion


        #region PROTECTED

        /// <summary>
        /// Sets the properties of interaction controller according to the properties of annotation.
        /// </summary>
        /// <param name="controller">The interaction controller.</param>
        protected override void SetInteractionControllerProperties(IInteractionController controller)
        {
            base.SetInteractionControllerProperties(controller);

            RectangularObjectTransformer rectangularTransformer = controller as RectangularObjectTransformer;
            if (rectangularTransformer != null)
            {
                rectangularTransformer.CanMove = Data.CanMove;
                rectangularTransformer.CanResize = Data.CanResize;
                rectangularTransformer.CanRotate = Data.CanRotate;
                return;
            }
        }

        /// <summary>
        /// Raises the <see cref="AnnotationView.StateChanged" /> event.
        /// Invoked when the property of annotation is changed.
        /// </summary>
        /// <param name="e">An <see cref="ObjectPropertyChangedEventArgs" />
        /// that contains the event data.</param>
        protected override void OnDataPropertyChanged(ObjectPropertyChangedEventArgs e)
        {
            base.OnDataPropertyChanged(e);

            if (e.PropertyName == "Size")
            {
                if (Builder is MarkAnnotationBuilder)
                    ((MarkAnnotationBuilder)Builder).InitialSize = (SizeF)e.NewValue;
            }
        }

        #endregion


        #region IRectangularInteractiveObject

        /// <summary>
        /// Returns a rectangle of interactive object.
        /// </summary>
        /// <param name="x0">Left-top X coordinate of rectangle.</param>
        /// <param name="y0">Left-top Y coordinate of rectangle.</param>
        /// <param name="x1">Right-bottom X coordinate of rectangle.</param>
        /// <param name="y1">Right-bottom Y coordinate of rectangle.</param>
        void IRectangularInteractiveObject.GetRectangle(
            out double x0,
            out double y0,
            out double x1,
            out double y1)
        {
            PointF location = Location;
            SizeF size = Size;
            x0 = location.X - size.Width / 2;
            y0 = location.Y - size.Height / 2;
            x1 = location.X + size.Width / 2;
            y1 = location.Y + size.Height / 2;
            if (Data.HorizontalMirrored)
            {
                double tmp = x0;
                x0 = x1;
                x1 = tmp;
            }
            if (Data.VerticalMirrored)
            {
                double tmp = y0;
                y0 = y1;
                y1 = tmp;
            }
        }

        /// <summary>
        /// Sets a rectangle of interactive object.
        /// </summary>
        /// <param name="x0">Left-top X coordinate of rectangle.</param>
        /// <param name="y0">Left-top Y coordinate of rectangle.</param>
        /// <param name="x1">Right-bottom X coordinate of rectangle.</param>
        /// <param name="y1">Right-bottom Y coordinate of rectangle.</param>
        void IRectangularInteractiveObject.SetRectangle(double x0, double y0, double x1, double y1)
        {
            Size = new SizeF((float)Math.Abs(x0 - x1), (float)Math.Abs(y0 - y1));
            Location = new PointF((float)(x0 + x1) / 2, (float)(y0 + y1) / 2);

            HorizontalMirrored = x0 > x1;
            VerticalMirrored = y0 > y1;

            if (Data.IsInitializing)
                OnStateChanged();
        }

        #endregion

        #endregion

    }
}

Imports System.ComponentModel
Imports System.Drawing
Imports System.Drawing.Drawing2D

Imports Vintasoft.Imaging
Imports Vintasoft.Imaging.Annotation
Imports Vintasoft.Imaging.Annotation.UI
Imports Vintasoft.Imaging.Annotation.UI.VisualTools.UserInteraction
Imports Vintasoft.Imaging.Drawing
Imports Vintasoft.Imaging.UI.VisualTools.UserInteraction

Namespace CommonCode.Annotation
    ''' <summary>
    ''' Class that determines how to display the annotation that displays a mark
    ''' and how user can interact with annotation.
    ''' </summary>
    Public Class MarkAnnotationView
        Inherits AnnotationView
        Implements IRectangularInteractiveObject

        #Region "Constructors"

        ''' <summary>
        ''' Initializes a new instance of the <see cref="MarkAnnotationView"/> class.
        ''' </summary>
        ''' <param name="annotationData">Object that stores the annotation data.</param>
        Public Sub New(annotationData As MarkAnnotationData)
            MyBase.New(annotationData)
            Dim initialSize As SizeF = Size
            If initialSize.IsEmpty Then
                initialSize = New Size(64, 64)
                Size = initialSize
            End If
            Builder = New MarkAnnotationBuilder(Me)

            Dim transformer__1 As New RectangularAnnotationTransformer(Me)
            transformer__1.HideInteractionPointsWhenMoving = True
            For Each point As InteractionPoint In transformer__1.ResizePoints
                point.FillColor = Color.FromArgb(100, Color.Red)
            Next
            Transformer = transformer__1
        End Sub

        #End Region



        #Region "Properties"

        ''' <summary>
        ''' Gets or sets a mark type.
        ''' </summary>
        <Description("The mark type.")> _
        <DefaultValue(MarkAnnotationType.Tick)> _
        Public Property MarkType() As MarkAnnotationType
            Get
                Return MarkAnnoData.MarkType
            End Get
            Set
                MarkAnnoData.MarkType = value
            End Set
        End Property

        ''' <summary>
        ''' Gets an annotation data.
        ''' </summary>
        Private ReadOnly Property MarkAnnoData() As MarkAnnotationData
            Get
                Return DirectCast(Data, MarkAnnotationData)
            End Get
        End Property

        ''' <summary>
        ''' Gets or sets the rotation angle of interactive object.
        ''' </summary>
        Private Property IRectangularInteractiveObject_RotationAngle() As Double Implements IRectangularInteractiveObject.RotationAngle
            Get
                Return Rotation
            End Get
            Set
                Rotation = CSng(value)
            End Set
        End Property

        #End Region



        #Region "Methods"

        #Region "PUBLIC"

        ''' <summary>
        ''' Indicates whether the specified point is contained within the annotation.
        ''' </summary>
        ''' <param name="point">Point in image space.</param>
        ''' <param name="ignoreContainmentCheckDistance">A value indicating whether value of <see cref="AnnotationView.ContainmentCheckDistance"/> property must be ignored.</param>
        ''' <returns><b>true</b> if the specified point is contained within the annotation;
        ''' otherwise, <b>false</b>.</returns>
        Public Overrides Function IsPointOnFigure(point As PointF, ignoreContainmentCheckDistance As Boolean) As Boolean
            Using path As IGraphicsPath = DirectCast(Renderer, MarkAnnotationRenderer).GetAsGraphicsPath(DrawingFactory.[Default])
                path.Transform(GetTransformFromContentToImageSpace())
                Using pen As IDrawingPen = DrawingFactory.[Default].CreatePen(Outline)
                    Return path.Contains(point) OrElse path.OutlineContains(point, pen)
                End Using
            End Using
        End Function

        ''' <summary>
        ''' Creates a new object that is a copy of the current
        ''' <see cref="MarkAnnotationView"/> instance.
        ''' </summary>
        ''' <returns>A new object that is a copy of this <see cref="MarkAnnotationView"/>
        ''' instance.</returns>
        Public Overrides Function Clone() As Object
            Return New MarkAnnotationView(DirectCast(Me.Data.Clone(), MarkAnnotationData))
        End Function

        ''' <summary>
        ''' Returns an annotation selection as <see cref="GraphicsPath"/> in annotation content space.
        ''' </summary>
        Public Overrides Function GetSelectionAsGraphicsPath() As GraphicsPath
            Dim path As New GraphicsPath()
            Dim size__1 As SizeF = Size
            path.AddRectangle(New RectangleF(-size__1.Width / 2, -size__1.Height / 2, size__1.Width, size__1.Height))
            Using transform As Matrix = GdiConverter.Convert(GetTransformFromContentToImageSpace())
                path.Transform(transform)
            End Using
            Return path
        End Function

        #End Region


        #Region "PROTECTED"

        ''' <summary>
        ''' Sets the properties of interaction controller according to the properties of annotation.
        ''' </summary>
        ''' <param name="controller">The interaction controller.</param>
        Protected Overrides Sub SetInteractionControllerProperties(controller As IInteractionController)
            MyBase.SetInteractionControllerProperties(controller)

            Dim rectangularTransformer As RectangularObjectTransformer = TryCast(controller, RectangularObjectTransformer)
            If rectangularTransformer IsNot Nothing Then
                rectangularTransformer.CanMove = Data.CanMove
                rectangularTransformer.CanResize = Data.CanResize
                rectangularTransformer.CanRotate = Data.CanRotate
                Return
            End If
        End Sub

        ''' <summary>
        ''' Raises the <see cref="AnnotationView.StateChanged" /> event.
        ''' Invoked when the property of annotation is changed.
        ''' </summary>
        ''' <param name="e">An <see cref="ObjectPropertyChangedEventArgs" />
        ''' that contains the event data.</param>
        Protected Overrides Sub OnDataPropertyChanged(e As ObjectPropertyChangedEventArgs)
            MyBase.OnDataPropertyChanged(e)

            If e.PropertyName = "Size" Then
                If TypeOf Builder Is MarkAnnotationBuilder Then
                    DirectCast(Builder, MarkAnnotationBuilder).InitialSize = CType(e.NewValue, SizeF)
                End If
            End If
        End Sub

        #End Region


        #Region "IRectangularInteractiveObject"

        ''' <summary>
        ''' Returns a rectangle of interactive object.
        ''' </summary>
        ''' <param name="x0">Left-top X coordinate of rectangle.</param>
        ''' <param name="y0">Left-top Y coordinate of rectangle.</param>
        ''' <param name="x1">Right-bottom X coordinate of rectangle.</param>
        ''' <param name="y1">Right-bottom Y coordinate of rectangle.</param>
        Private Sub IRectangularInteractiveObject_GetRectangle(ByRef x0 As Double, ByRef y0 As Double, ByRef x1 As Double, ByRef y1 As Double) Implements IRectangularInteractiveObject.GetRectangle
            Dim location__1 As PointF = Location
            Dim size__2 As SizeF = Size
            x0 = location__1.X - size__2.Width / 2
            y0 = location__1.Y - size__2.Height / 2
            x1 = location__1.X + size__2.Width / 2
            y1 = location__1.Y + size__2.Height / 2
            If Data.HorizontalMirrored Then
                Dim tmp As Double = x0
                x0 = x1
                x1 = tmp
            End If
            If Data.VerticalMirrored Then
                Dim tmp As Double = y0
                y0 = y1
                y1 = tmp
            End If
        End Sub

        ''' <summary>
        ''' Sets a rectangle of interactive object.
        ''' </summary>
        ''' <param name="x0">Left-top X coordinate of rectangle.</param>
        ''' <param name="y0">Left-top Y coordinate of rectangle.</param>
        ''' <param name="x1">Right-bottom X coordinate of rectangle.</param>
        ''' <param name="y1">Right-bottom Y coordinate of rectangle.</param>
        Private Sub IRectangularInteractiveObject_SetRectangle(x0 As Double, y0 As Double, x1 As Double, y1 As Double) Implements IRectangularInteractiveObject.SetRectangle
            Size = New SizeF(CSng(Math.Abs(x0 - x1)), CSng(Math.Abs(y0 - y1)))
            Location = New PointF(CSng(x0 + x1) / 2, CSng(y0 + y1) / 2)

            HorizontalMirrored = x0 > x1
            VerticalMirrored = y0 > y1

            If Data.IsInitializing Then
                OnStateChanged()
            End If
        End Sub

        #End Region

        #End Region

    End Class
End Namespace



Registering link between MarkAnnotationData and MarkAnnotationView classes

Link between MarkAnnotationData and MarkAnnotationView classes should be registered before any instance of MarkAnnotationView class will be created. Link between MarkAnnotationData and MarkAnnotationView classes can be registered using the AnnotationViewFactory.RegisterViewForAnnotationData method.

MarkAnnotationRenderer class

MarkAnnotationRenderer class:
Here is C#/VB.NET code that shows implementation of MarkAnnotationRenderer class:
using System.Drawing;

using Vintasoft.Imaging;

using Vintasoft.Imaging.Annotation;
using Vintasoft.Imaging.Annotation.Rendering;
using Vintasoft.Imaging.Drawing;

namespace CommonCode.Annotation
{
    /// <summary>
    /// Determines how to render the mark annotation.
    /// </summary>
    public class MarkAnnotationRenderer : AnnotationRenderer
    {

        #region Constructors

        /// <summary>
        /// Initializes a new instance of the <see cref="MarkAnnotationRenderer"/> class.
        /// </summary>
        /// <param name="annotationData">Object that stores the annotation data.</param>
        public MarkAnnotationRenderer(MarkAnnotationData annotationData)
            : base(annotationData)
        {
        }

        #endregion



        #region Properties

        /// <summary>
        /// Gets an annotation data.
        /// </summary>
        MarkAnnotationData MarkAnnoData
        {
            get
            {
                return (MarkAnnotationData)Data;
            }
        }

        #endregion



        #region Methods

        /// <summary>
        /// Returns a drawing box of annotation, in the image space.
        /// </summary>
        /// <param name="drawingSurface">The object that provides information about drawing surface.</param>
        /// <returns>Drawing box of annotation, in the image space.</returns>
        public override RectangleF GetDrawingBox(DrawingSurface drawingSurface)
        {
            using (IGraphicsPath path = GetAsGraphicsPath(DrawingFactory.Default))
            {
                using (IDrawingPen pen = DrawingFactory.Default.CreatePen(Data.Outline))
                {
                    // create transformation that allows to get correct bounding box
                    AffineMatrix transform = AffineMatrix.CreateRotation(MarkAnnoData.Rotation);
                    transform.Translate(MarkAnnoData.Location.X, MarkAnnoData.Location.Y);

                    return path.GetBounds(pen, transform);
                }
            }
        }

        /// <summary>
        /// Returns a mark annotation as <see cref="IGraphicsPath"/> in content space.
        /// </summary>
        public virtual IGraphicsPath GetAsGraphicsPath(DrawingFactory drawingFactory)
        {
            IGraphicsPath path = drawingFactory.CreateGraphicsPath();

            PointF[] referencePoints = MarkAnnoData.GetReferencePointsInContentSpace();

            switch (MarkAnnoData.MarkType)
            {
                case MarkAnnotationType.Tick:
                    path.AddCurve(referencePoints);
                    break;
                default:
                    path.AddPolygon(referencePoints);
                    break;
            }

            return path;
        }

        /// <summary>
        /// Renders the annotation on the <see cref="T:Vintasoft.Imaging.Drawing.DrawingEngine" />
        /// in the coordinate space of annotation.
        /// </summary>
        /// <param name="drawingEngine">The <see cref="T:Vintasoft.Imaging.Drawing.DrawingEngine" /> to render on.</param>
        /// <param name="drawingSurface">The object that provides information about drawing surface.</param>
        protected override void RenderInContentSpace(DrawingEngine drawingEngine, DrawingSurface drawingSurface)
        {
            using (IGraphicsPath path = GetAsGraphicsPath(drawingEngine.DrawingFactory))
            {
                if (Data.FillBrush != null)
                {
                    using (IDrawingBrush brush = drawingEngine.DrawingFactory.CreateBrush(Data.FillBrush))
                        drawingEngine.FillPath(brush, path);
                }
                if (Data.Border)
                {
                    using (IDrawingPen pen = drawingEngine.DrawingFactory.CreatePen(Data.Outline))
                        drawingEngine.DrawPath(pen, path);
                }
            }
        }

        #endregion

    }
}

Imports System.Drawing

Imports Vintasoft.Imaging

Imports Vintasoft.Imaging.Annotation
Imports Vintasoft.Imaging.Annotation.Rendering
Imports Vintasoft.Imaging.Drawing

Namespace CommonCode.Annotation
    ''' <summary>
    ''' Determines how to render the mark annotation.
    ''' </summary>
    Public Class MarkAnnotationRenderer
        Inherits AnnotationRenderer

        #Region "Constructors"

        ''' <summary>
        ''' Initializes a new instance of the <see cref="MarkAnnotationRenderer"/> class.
        ''' </summary>
        ''' <param name="annotationData">Object that stores the annotation data.</param>
        Public Sub New(annotationData As MarkAnnotationData)
            MyBase.New(annotationData)
        End Sub

        #End Region



        #Region "Properties"

        ''' <summary>
        ''' Gets an annotation data.
        ''' </summary>
        Private ReadOnly Property MarkAnnoData() As MarkAnnotationData
            Get
                Return DirectCast(Data, MarkAnnotationData)
            End Get
        End Property

        #End Region



        #Region "Methods"

        ''' <summary>
        ''' Returns a drawing box of annotation, in the image space.
        ''' </summary>
        ''' <param name="drawingSurface">The object that provides information about drawing surface.</param>
        ''' <returns>Drawing box of annotation, in the image space.</returns>
        Public Overrides Function GetDrawingBox(drawingSurface As DrawingSurface) As RectangleF
            Using path As IGraphicsPath = GetAsGraphicsPath(DrawingFactory.[Default])
                Using pen As IDrawingPen = DrawingFactory.[Default].CreatePen(Data.Outline)
                    ' create transformation that allows to get correct bounding box
                    Dim transform As AffineMatrix = AffineMatrix.CreateRotation(MarkAnnoData.Rotation)
                    transform.Translate(MarkAnnoData.Location.X, MarkAnnoData.Location.Y)

                    Return path.GetBounds(pen, transform)
                End Using
            End Using
        End Function

        ''' <summary>
        ''' Returns a mark annotation as <see cref="IGraphicsPath"/> in content space.
        ''' </summary>
        Public Overridable Function GetAsGraphicsPath(drawingFactory As DrawingFactory) As IGraphicsPath
            Dim path As IGraphicsPath = drawingFactory.CreateGraphicsPath()

            Dim referencePoints As PointF() = MarkAnnoData.GetReferencePointsInContentSpace()

            Select Case MarkAnnoData.MarkType
                Case MarkAnnotationType.Tick
                    path.AddCurve(referencePoints)
                    Exit Select
                Case Else
                    path.AddPolygon(referencePoints)
                    Exit Select
            End Select

            Return path
        End Function

        ''' <summary>
        ''' Renders the annotation on the <see cref="T:Vintasoft.Imaging.Drawing.DrawingEngine" />
        ''' in the coordinate space of annotation.
        ''' </summary>
        ''' <param name="drawingEngine">The <see cref="T:Vintasoft.Imaging.Drawing.DrawingEngine" /> to render on.</param>
        ''' <param name="drawingSurface">The object that provides information about drawing surface.</param>
        Protected Overrides Sub RenderInContentSpace(drawingEngine As DrawingEngine, drawingSurface As DrawingSurface)
            Using path As IGraphicsPath = GetAsGraphicsPath(drawingEngine.DrawingFactory)
                If Data.FillBrush IsNot Nothing Then
                    Using brush As IDrawingBrush = drawingEngine.DrawingFactory.CreateBrush(Data.FillBrush)
                        drawingEngine.FillPath(brush, path)
                    End Using
                End If
                If Data.Border Then
                    Using pen As IDrawingPen = drawingEngine.DrawingFactory.CreatePen(Data.Outline)
                        drawingEngine.DrawPath(pen, path)
                    End Using
                End If
            End Using
        End Sub

        #End Region

    End Class
End Namespace






Examples

The implementation of mark annotation in C# and VB.NET is a part of AnnotationDemo and WpfAnnotationDemo project code.