DynamicRenderer.cs source code in C# .NET

Source code for the .NET framework in C#

                        

Code:

/ DotNET / DotNET / 8.0 / untmp / WIN_WINDOWS / lh_tools_devdiv_wpf / Windows / wcp / Core / System / Windows / Input / Stylus / DynamicRenderer.cs / 2 / DynamicRenderer.cs

                            //#define DEBUG_RENDERING_FEEDBACK 
//----------------------------------------------------------------------------
//
// File: DynamicRenderer.cs
// 
// Description:
//      DynamicRenderer PlugIn - Provides off (and on) app Dispatcher Inking support. 
// 
// Copyright (C) 2003-2004 by Microsoft Corporation.  All rights reserved.
// 
//---------------------------------------------------------------------------

using System;
using System.Diagnostics; 
using System.Collections.Specialized;
using System.Collections.Generic; 
using System.Windows; 
using System.Windows.Input;
using System.Windows.Media; 
using System.Threading;
using System.Windows.Threading;
using MS.Utility;
using System.Windows.Ink; 
using MS.Internal.Ink;
using System.Security; 
 
using SR=MS.Internal.PresentationCore.SR;
using SRID=MS.Internal.PresentationCore.SRID; 

namespace System.Windows.Input.StylusPlugIns
{
    ///////////////////////////////////////////////////////////////////////// 
    /// 
    /// [TBS] 
    ///  
    public class DynamicRenderer : StylusPlugIn
    { 

        /////////////////////////////////////////////////////////////////////

        private class StrokeInfo 
        {
            int _stylusId; 
            int _startTime; 
            int _lastTime;
            ContainerVisual _strokeCV;  // App thread rendering CV 
            ContainerVisual _strokeRTICV; // real time input CV
            bool _seenUp; // Have we seen the stylusUp event yet?
            bool _isReset; // Was reset used to create this StrokeInfo?
            SolidColorBrush _fillBrush; // app thread based brushed 
            DrawingAttributes _drawingAttributes;
            StrokeNodeIterator _strokeNodeIterator; 
            double _opacity; 
            DynamicRendererHostVisual   _strokeHV;  // App thread rendering HostVisual
 
            public StrokeInfo(DrawingAttributes drawingAttributes, int stylusDeviceId, int startTimestamp, DynamicRendererHostVisual hostVisual)
            {
                _stylusId = stylusDeviceId;
                _startTime = startTimestamp; 
                _lastTime = _startTime;
                _drawingAttributes = drawingAttributes.Clone(); // stroke copy for duration of stroke. 
                _strokeNodeIterator = new StrokeNodeIterator(_drawingAttributes); 
                Color color = _drawingAttributes.Color;
                _opacity = _drawingAttributes.IsHighlighter ? 0 : (double)color.A / (double)StrokeRenderer.SolidStrokeAlpha; 
                color.A = StrokeRenderer.SolidStrokeAlpha;

                // Set the brush to be used with this new stroke too (since frozen can be shared by threads)
                SolidColorBrush brush = new SolidColorBrush(color); 
                brush.Freeze();
                _fillBrush = brush; 
                _strokeHV = hostVisual; 
                hostVisual.AddStrokeInfoRef(this); // Add ourselves as reference.
            } 

            // Public props to access info
            public int StylusId
            { 
                get { return _stylusId; }
            } 
            public int StartTime 
            {
                get { return _startTime; } 
            }
            public int LastTime
            {
                get { return _lastTime; } 
                set { _lastTime = value; }
            } 
            public ContainerVisual StrokeCV 
            {
                get { return _strokeCV; } 
                set { _strokeCV = value; }
            }
            public ContainerVisual StrokeRTICV
            { 
                get { return _strokeRTICV; }
                set { _strokeRTICV = value; } 
            } 
            public bool SeenUp
            { 
                get { return _seenUp; }
                set { _seenUp = value; }
            }
            public bool IsReset 
            {
                get { return _isReset; } 
                set { _isReset = value; } 
            }
            public StrokeNodeIterator StrokeNodeIterator 
            {
                get { return _strokeNodeIterator; }
                set
                { 
                    if (value == null)
                    { 
                        throw new ArgumentNullException(); 
                    }
                    _strokeNodeIterator = value; 
                }
            }
            public SolidColorBrush FillBrush
            { 
                get { return _fillBrush; }
                set { _fillBrush = value; } 
            } 
            public DrawingAttributes DrawingAttributes
            { 
                get { return _drawingAttributes; }
            }
            public double Opacity
            { 
                get { return _opacity; }
            } 
            public DynamicRendererHostVisual StrokeHV 
            {
                get { return _strokeHV; } 
            }

            // See if timestamp is part of this stroke.  Deals with tickcount wrapping.
            public bool IsTimestampWithin(int timestamp) 
            {
                // If we've seen up use the start and end to figure out if timestamp 
                // is between start and last.  Note that we need to deal with the 
                // times wrapping back to 0.
                if (SeenUp) 
                {
                    if (StartTime < LastTime) // wrapping check
                    {
                        return ((timestamp >= StartTime) && (timestamp <= LastTime)); 
                    }
                    else // The timestamp wrapped back to zero 
                    { 
                        return ((timestamp >= StartTime) || (timestamp <= LastTime));
                    } 
                }
                else
                {
                    return true; // everything is consider part of an open StrokeInfo. 
                }
            } 
 
            // See if a new timestamp adding at the end of this stroke.  Deals with tickcount wrapping.
            public bool IsTimestampAfter(int timestamp) 
            {
                // If we've seen up then timestamp can't be after, otherwise do the check.
                // Note that we need to deal with the times wrapping (goes negative).
                if (!SeenUp) 
                {
                    if (LastTime >= StartTime) 
                    { 
                        if (timestamp >= LastTime)
                        { 
                            return true;
                        }
                        else
                        { 
                            return ((LastTime > 0) && (timestamp < 0));  // true if we wrapped
                        } 
                    } 
                    else // The timestamp may have wrapped, see if greater than last time and less than start time
                    { 
                        return timestamp >= LastTime && timestamp <= StartTime;
                    }
                }
                else 
                {
                    return false; // Nothing can be after a closed StrokeInfo (see up). 
                } 
            }
 
        }

        private class DynamicRendererHostVisual : HostVisual
        { 
            internal bool InUse
            { 
                get { return _strokeInfoList.Count > 0; } 
            }
            internal bool HasSingleReference 
            {
                get { return _strokeInfoList.Count == 1; }
            }
            internal void AddStrokeInfoRef(StrokeInfo si) 
            {
                _strokeInfoList.Add(si); 
            } 
            internal void RemoveStrokeInfoRef(StrokeInfo si)
            { 
                _strokeInfoList.Remove(si);
            }

            ///  
            /// Critical - Calls SecurityCritical method with LinkDemand (CompositionTarget.RootVisual).
            /// TreatAsSafe: You can't set the RootVisual to an arbitrary value. 
            ///  
            internal VisualTarget VisualTarget
            { 
                [SecurityCritical, SecurityTreatAsSafe]
                get
                {
                    if (_visualTarget == null) 
                    {
                        _visualTarget = new VisualTarget(this); 
                        _visualTarget.RootVisual = new ContainerVisual(); 
                    }
                    return _visualTarget; 
                }
            }

            VisualTarget       _visualTarget; 
            List   _strokeInfoList = new List();
        } 
 
        /////////////////////////////////////////////////////////////////////
 
        /// 
        /// [TBS] - On UIContext
        /// 
        public DynamicRenderer() : base() 
        {
            _zeroSizedFrozenRect = new RectangleGeometry(new Rect(0,0,0,0)); 
            _zeroSizedFrozenRect.Freeze(); 
        }
 
        /////////////////////////////////////////////////////////////////////
        /// 
        /// Reset will stop the current strokes being dynamically rendered
        /// and start a new stroke with the packets passed in.  Specified StylusDevice 
        /// must be in down position when calling this method.
        /// Only call from application dispatcher. 
        ///  
        /// 
        ///  
        public virtual void Reset(StylusDevice stylusDevice, StylusPointCollection stylusPoints)
        {
            // NOTE: stylusDevice == null means the mouse device.
 
            // Nothing to do if root visual not queried or not hookup up to element yet.
            if (_mainContainerVisual == null || _applicationDispatcher == null || !IsActiveForInput) 
                return; 

            // Ensure on UIContext. 
            _applicationDispatcher.VerifyAccess();

            // Make sure the stylusdevice specified (or mouse if null stylusdevice) is currently in
            // down state! 
            bool inAir = (stylusDevice != null) ?
                            stylusDevice.InAir : 
                            Mouse.PrimaryDevice.LeftButton == MouseButtonState.Released; 

            if (inAir) 
            {
                throw new ArgumentException(SR.Get(SRID.Stylus_MustBeDownToCallReset), "stylusDevice");
            }
 
            // Avoid reentrancy due to lock() call.
            using(_applicationDispatcher.DisableProcessing()) 
            { 
                lock(__siLock)
                { 
                    AbortAllStrokes(); // stop any current inking strokes

                    // Now create new si and insert it in the list.
                    StrokeInfo si = new StrokeInfo(DrawingAttributes, 
                                                   (stylusDevice != null) ? stylusDevice.Id : 0,
                                                   Environment.TickCount, GetCurrentHostVisual()); 
                    _strokeInfoList.Add(si); 
                    si.IsReset = true;
 
                    if (stylusPoints != null)
                    {
                        RenderPackets(stylusPoints, si); // do this inside of lock to make sure this renders first.
                    } 
                }
            } 
        } 

        ///////////////////////////////////////////////////////////////////// 
        /// 
        /// [TBS] - On app Dispatcher
        /// 
        public Visual RootVisual 
        {
            get 
            { 
                // NOTE: We don't create any visuals (real time or non real time) until someone
                //  queries for this property since we can't display anything until this is done and 
                // they hook the returned visual up to their visual tree.
                if (_mainContainerVisual == null)
                {
                    CreateInkingVisuals(); // ensure at least the app dispatcher visuals are created. 
                }
                return _mainContainerVisual; 
            } 
        }
 
        /////////////////////////////////////////////////////////////////////
        /// 
        /// [TBS] - On app Dispatcher
        ///  
        protected override void OnAdded()
        { 
            // Grab the dispatcher we're hookup up to. 
            _applicationDispatcher = Element.Dispatcher;
 
            // If we are active for input, make sure we create the real time inking thread
            // and visuals if needed.
            if (IsActiveForInput)
            { 
                CreateRealTimeVisuals();  // Transitions to inking thread.
            } 
        } 

        ///////////////////////////////////////////////////////////////////// 
        /// 
        /// [TBS] - On app dispatcher
        /// 
        protected override void OnRemoved() 
        {
            // Make sure we destroy any real time visuals and thread when removed. 
            DestroyRealTimeVisuals(); 
            _applicationDispatcher = null; // removed from tree.
        } 

        /////////////////////////////////////////////////////////////////////
        /// 
        /// [TBS] - On UIContext 
        /// 
        protected override void OnIsActiveForInputChanged() 
        { 
            // We only want to keep our real time inking thread references around only
            // when we need them.  If not enabled for input then we don't need them. 
            if (IsActiveForInput)
            {
                // Make sure we create the real time inking visuals if we in tree.
                CreateRealTimeVisuals();  // Transitions to inking thread. 
            }
            else 
            { 
                DestroyRealTimeVisuals();
            } 
        }

        /////////////////////////////////////////////////////////////////////
        ///  
        /// [TBS] - On pen threads or app thread
        ///  
        protected override void OnStylusEnter(RawStylusInput rawStylusInput, bool confirmed) 
        {
            HandleStylusEnterLeave(rawStylusInput, true, confirmed); 
        }

        /////////////////////////////////////////////////////////////////////
        ///  
        /// [TBS] - On pen threads or app thread
        ///  
        protected override void OnStylusLeave(RawStylusInput rawStylusInput, bool confirmed) 
        {
            HandleStylusEnterLeave(rawStylusInput, false, confirmed); 
        }

        private void HandleStylusEnterLeave(RawStylusInput rawStylusInput, bool isEnter, bool isConfirmed)
        { 
            // See if we need to abort a stroke due to entering or leaving within a stroke.
            if (isConfirmed) 
            { 
                StrokeInfo si = FindStrokeInfo(rawStylusInput.Timestamp);
 
                if (si != null)
                {
                    if (rawStylusInput.StylusDeviceId == si.StylusId)
                    { 
                        if ((isEnter && (rawStylusInput.Timestamp > si.StartTime)) ||
                            (!isEnter && !si.SeenUp)) 
                        { 
                            // abort this stroke.
                            TransitionStrokeVisuals(si, true); 
                        }
                    }
                }
            } 
        }
 
        ///////////////////////////////////////////////////////////////////// 
        /// 
        /// [TBS] - On UIContext 
        /// 
        protected override void OnEnabledChanged()
        {
            // If going disabled cancel all real time strokes.  We won't be getting any more 
            // events.
            if (!Enabled) 
            { 
                AbortAllStrokes();
            } 
        }

        /////////////////////////////////////////////////////////////////////
        ///  
        /// [TBS]
        ///  
        protected override void OnStylusDown(RawStylusInput rawStylusInput) 
        {
            // Only allow inking if someone has queried our RootVisual. 
            if (_mainContainerVisual != null)
            {
                StrokeInfo si;
 
                lock(__siLock)
                { 
                    si = FindStrokeInfo(rawStylusInput.Timestamp); 

                    // If we find we are already in the middle of stroke then bail out. 
                    // Can only ink with one stylus at a time.
                    if (si != null)
                    {
                        return; 
                    }
 
                    si = new StrokeInfo(DrawingAttributes, rawStylusInput.StylusDeviceId, rawStylusInput.Timestamp, GetCurrentHostVisual()); 
                    _strokeInfoList.Add(si);
                } 

                rawStylusInput.NotifyWhenProcessed(si);
                RenderPackets(rawStylusInput.GetStylusPoints(), si);
            } 
        }
 
        ///////////////////////////////////////////////////////////////////// 
        /// 
        /// [TBS] 
        /// 
        protected override void OnStylusMove(RawStylusInput rawStylusInput)
        {
            // Only allow inking if someone has queried our RootVisual. 
            if (_mainContainerVisual != null)
            { 
                StrokeInfo si = FindStrokeInfo(rawStylusInput.Timestamp); 

                if (si != null && (si.StylusId == rawStylusInput.StylusDeviceId)) 
                {
                    // We only render packets that are in the proper order due to
                    // how our incremental rendering uses the last point to continue
                    // the path geometry from. 
                    // NOTE: We also update the LastTime value here too
                    if (si.IsTimestampAfter(rawStylusInput.Timestamp)) 
                    { 
                        si.LastTime = rawStylusInput.Timestamp;
                        RenderPackets(rawStylusInput.GetStylusPoints(), si); 
                    }
                }
            }
        } 

        ///////////////////////////////////////////////////////////////////// 
        ///  
        /// [TBS]
        ///  
        protected override void OnStylusUp(RawStylusInput rawStylusInput)
        {
            // Only allow inking if someone has queried our RootVisual.
            if (_mainContainerVisual != null) 
            {
                StrokeInfo si = FindStrokeInfo(rawStylusInput.Timestamp); 
 
                if (si != null &&
                    ((si.StylusId == rawStylusInput.StylusDeviceId) || 
                     (rawStylusInput.StylusDeviceId == 0 &&
                      (si.IsReset ||
                      (si.IsTimestampAfter(rawStylusInput.Timestamp) && IsStylusUp(si.StylusId))))))
                { 
                    si.SeenUp = true;
                    si.LastTime = rawStylusInput.Timestamp; 
                    rawStylusInput.NotifyWhenProcessed(si); 
                }
            } 
        }

        private bool IsStylusUp(int stylusId)
        { 
            TabletDeviceCollection tabletDevices = Tablet.TabletDevices;
            for (int i=0; i
        /// [TBS] 
        /// 
        private void OnRenderComplete() 
        { 
            StrokeInfo si = _renderCompleteStrokeInfo;
            Debug.Assert(si!=null);  // should never get here unless we are transitioning a stroke. 

            if (si != null)
            {
                // See if we are done transitioning this stroke!! 
                if (si.StrokeHV.Clip == null)
                { 
                    TransitionComplete(si); 
                    _renderCompleteStrokeInfo = null;
                } 
                else
                {
                    // Wait for real time visual to be removed and updated.
                    RemoveDynamicRendererVisualAndNotifyWhenDone(si); 
                }
            } 
        } 

        void RemoveDynamicRendererVisualAndNotifyWhenDone(StrokeInfo si) 
        {
            if (si != null)
            {
                DynamicRendererThreadManager renderingThread = _renderingThread; // Keep it alive 
                if (renderingThread != null)
                { 
                    // We are being called by the main UI thread, so marshal over to 
                    // the inking thread before cleaning up the stroke visual.
                    renderingThread.ThreadDispatcher.BeginInvoke(DispatcherPriority.Send, 
                    (DispatcherOperationCallback)delegate(object unused)
                    {
                        if (si.StrokeRTICV != null)
                        { 
                            // Now wait till this is rendered and then notify UI thread.
                            if (_onDRThreadRenderComplete == null) 
                            { 
                                _onDRThreadRenderComplete = new EventHandler(OnDRThreadRenderComplete);
                            } 

                            // Add to list to transact.
                            _renderCompleteDRThreadStrokeInfoList.Enqueue(si);
 
                            // See if we are already waiting for a removed stroke to be rendered.
                            // If we aren't then remove visuals and wait for it to be rendered. 
                            // Otherwise we'll do the work when the current stroke has been removed. 
                            if (!_waitingForDRThreadRenderComplete)
                            { 
                                ((ContainerVisual)si.StrokeHV.VisualTarget.RootVisual).Children.Remove(si.StrokeRTICV);
                                si.StrokeRTICV = null;

                                // hook up render complete notification for one time then unhook. 
                                MediaContext.From(renderingThread.ThreadDispatcher).RenderComplete += _onDRThreadRenderComplete;
                                _waitingForDRThreadRenderComplete = true; 
                            } 
                        }
                        else 
                        {
                            // Nothing to transition so just say we're done!
                            NotifyAppOfDRThreadRenderComplete(si);
                        } 

                        return null; 
                    }, 
                    null);
                } 
            }
        }

 
        private void NotifyAppOfDRThreadRenderComplete(StrokeInfo si)
        { 
            Dispatcher dispatcher = _applicationDispatcher; 
            if (dispatcher != null)
            { 
                // We are being called by the inking thread, so marshal over to
                // the UI thread before handling the StrokeInfos that are done rendering.
                dispatcher.BeginInvoke(DispatcherPriority.Send,
                (DispatcherOperationCallback)delegate(object unused) 
                {
                    // See if this is the one we are doing a full transition for. 
                    if (si == _renderCompleteStrokeInfo) 
                    {
                        if (si.StrokeHV.Clip != null) 
                        {
                            si.StrokeHV.Clip = null;
                            NotifyOnNextRenderComplete();
                        } 
                        else
                        { 
                            Debug.Assert(_waitingForRenderComplete, "We were expecting to be waiting for a RenderComplete to call our OnRenderComplete, we might never reset and get flashing strokes from here on out"); 
                            TransitionComplete(si); // We're done
                        } 
                    }
                    else
                    {
                        TransitionComplete(si); // We're done 
                    }
                    return null; 
                }, 
                null);
            } 
        }


        private void OnDRThreadRenderComplete(object sender, EventArgs e) 
        {
            DynamicRendererThreadManager drThread = _renderingThread; 
            Dispatcher drDispatcher = null; 

            // Remove RenderComplete hook. 
            if (drThread != null)
            {
                drDispatcher = drThread.ThreadDispatcher;
 
                if (drDispatcher != null)
                { 
                    if (_renderCompleteDRThreadStrokeInfoList.Count > 0) 
                    {
                        StrokeInfo si = _renderCompleteDRThreadStrokeInfoList.Dequeue(); 
                        NotifyAppOfDRThreadRenderComplete(si);
                    }

                    // If no other queued up transitions, then remove event listener. 
                    if (_renderCompleteDRThreadStrokeInfoList.Count == 0)
                    { 
                        // First unhook event handler 
                        MediaContext.From(drDispatcher).RenderComplete -= _onDRThreadRenderComplete;
                        _waitingForDRThreadRenderComplete = false; 
                    }
                    else
                    {
                        // Process next waiting one.  Note we don't remove till removed processed. 
                        StrokeInfo siNext = _renderCompleteDRThreadStrokeInfoList.Peek();
                        if (siNext.StrokeRTICV != null) 
                        { 
                            // Post this back to our thread to make sure we return from the
                            // this render complete call first before queuing up the next. 
                            drDispatcher.BeginInvoke(DispatcherPriority.Send,
                            (DispatcherOperationCallback)delegate(object unused)
                            {
                                ((ContainerVisual)siNext.StrokeHV.VisualTarget.RootVisual).Children.Remove(siNext.StrokeRTICV); 
                                siNext.StrokeRTICV = null;
                                return null; 
                            }, 
                            null);
                        } 
                    }
                }
            }
        } 

 
        ///////////////////////////////////////////////////////////////////// 
        /// 
        /// [TBS] 
        /// 
        protected override void OnStylusDownProcessed(object callbackData, bool targetVerified)
        {
            StrokeInfo si = callbackData as StrokeInfo; 

            if (si == null) 
                return; 

            // See if we need to abort this stroke or reset the HostVisual clipping rect to null. 
            if (!targetVerified)
            {
                TransitionStrokeVisuals(si, true);
            } 
        }
 
        ///////////////////////////////////////////////////////////////////// 
        /// 
        /// [TBS] 
        /// 
        protected override void OnStylusUpProcessed(object callbackData, bool targetVerified)
        {
            StrokeInfo si = callbackData as StrokeInfo; 

            if (si == null) 
                return; 

            // clean up stroke visuals (and move to transitional VisualTarget as needed) 
            TransitionStrokeVisuals(si, !targetVerified);
        }

        private void OnInternalRenderComplete(object sender, EventArgs e) 
        {
            // First unhook event handler 
            MediaContext.From(_applicationDispatcher).RenderComplete -= _onRenderComplete; 
            _waitingForRenderComplete = false;
 
            // Make sure lock() doesn't cause reentrancy.
            using(_applicationDispatcher.DisableProcessing())
            {
                // Now notify event happened. 
                OnRenderComplete();
            } 
        } 

 
        /////////////////////////////////////////////////////////////////////
        /// 
        /// [TBS]
        ///  
        private void NotifyOnNextRenderComplete()
        { 
            // Nothing to do if not hooked up to plugin collection. 
            if (_applicationDispatcher == null)
                return; 

            // Ensure on application Dispatcher.
            _applicationDispatcher.VerifyAccess();
 
            if (_onRenderComplete == null)
            { 
                _onRenderComplete = new EventHandler(OnInternalRenderComplete); 
            }
 
            if (!_waitingForRenderComplete)
            {
                // hook up render complete notification for one time then unhook.
                MediaContext.From(_applicationDispatcher).RenderComplete += _onRenderComplete; 
                _waitingForRenderComplete = true;
            } 
        } 

        ///////////////////////////////////////////////////////////////////// 
        /// 
        /// [TBS]
        /// 
        protected virtual void OnDraw(  DrawingContext drawingContext, 
                                        StylusPointCollection stylusPoints,
                                        Geometry geometry, 
                                        Brush fillBrush) 
        {
            if (drawingContext == null) 
            {
                throw new ArgumentNullException("drawingContext");
            }
            drawingContext.DrawGeometry(fillBrush, null, geometry); 
        }
 
        ///////////////////////////////////////////////////////////////////// 
        /// 
        /// [TBS] 
        /// 
        protected virtual void OnDrawingAttributesReplaced()
        {
        } 

        ///////////////////////////////////////////////////////////////////// 
        ///  
        /// Retrieves the Dispatcher for the thread used for rendering dynamic strokes
        /// when receiving data from the stylus input thread(s). 
        /// 
        protected Dispatcher GetDispatcher()
        {
            return _renderingThread != null ? _renderingThread.ThreadDispatcher : null; 
        }
 
        ///////////////////////////////////////////////////////////////////// 

        void RenderPackets(StylusPointCollection stylusPoints,  StrokeInfo si) 
        {
            // If no points or not hooked up to element then do nothing.
            if (stylusPoints.Count == 0 || _applicationDispatcher == null)
                return; 

            // Get a collection of ink nodes built from the new stylusPoints. 
            si.StrokeNodeIterator = si.StrokeNodeIterator.GetIteratorForNextSegment(stylusPoints); 
            if (si.StrokeNodeIterator != null)
            { 
                // Create a PathGeometry representing the contour of the ink increment
                Geometry strokeGeometry;
                Rect bounds;
                StrokeRenderer.CalcGeometryAndBounds(si.StrokeNodeIterator, 
                                                     si.DrawingAttributes,
#if DEBUG_RENDERING_FEEDBACK 
                                                     null, //debug dc 
                                                     0d,   //debug feedback size
                                                     false,//render debug feedback 
#endif
                                                     false, //calc bounds
                                                     out strokeGeometry,
                                                     out bounds); 

                // If we are called from the app thread we can just stay on it and render to that 
                // visual tree.  Otherwise we need to marshal over to our inking thread to do our work. 
                if (_applicationDispatcher.CheckAccess())
                { 
                    // See if we need to create a new container visual for the stroke.
                    if (si.StrokeCV == null)
                    {
                        // Create new container visual for this stroke and add our incremental rendering visual to it. 
                        si.StrokeCV = new ContainerVisual();
 
                        // 

 


                        if (!si.DrawingAttributes.IsHighlighter)
                        { 
                            si.StrokeCV.Opacity = si.Opacity;
                        } 
                        _mainRawInkContainerVisual.Children.Add(si.StrokeCV); 
                    }
 
                    // Create new visual and render the geometry into it
                    DrawingVisual visual = new DrawingVisual();
                    DrawingContext drawingContext = visual.RenderOpen();
                    try 
                    {
                        OnDraw(drawingContext, stylusPoints, strokeGeometry, si.FillBrush); 
                    } 
                    finally
                    { 
                        drawingContext.Close();
                    }

                    // Now add it to the visual tree (making sure we still have StrokeCV after 
                    // onDraw called above).
                    if (si.StrokeCV != null) 
                    { 
                        si.StrokeCV.Children.Add(visual);
                    } 
                }
                else
                {
                    DynamicRendererThreadManager renderingThread = _renderingThread; // keep it alive 
                    Dispatcher drDispatcher = renderingThread != null ? renderingThread.ThreadDispatcher : null;
 
                    // Only try to render if we get a ref on the rendering thread. 
                    if (drDispatcher != null)
                    { 
                        // We are on a pen thread so marshal this call to our inking thread.
                        drDispatcher.BeginInvoke(DispatcherPriority.Send,
                        (DispatcherOperationCallback) delegate(object unused)
                        { 
                            SolidColorBrush fillBrush = si.FillBrush;
 
                            // Make sure this stroke is not aborted 
                            if (fillBrush != null)
                            { 
                                // See if we need to create a new container visual for the stroke.
                                if (si.StrokeRTICV == null)
                                {
                                    // Create new container visual for this stroke and add our incremental rendering visual to it. 
                                    si.StrokeRTICV = new ContainerVisual();
 
                                    // 

 


                                    if (!si.DrawingAttributes.IsHighlighter)
                                    { 
                                        si.StrokeRTICV.Opacity = si.Opacity;
                                    } 
                                    ((ContainerVisual)si.StrokeHV.VisualTarget.RootVisual).Children.Add(si.StrokeRTICV); 
                                }
 
                                // Create new visual and render the geometry into it
                                DrawingVisual visual = new DrawingVisual();
                                DrawingContext drawingContext = visual.RenderOpen();
                                try 
                                {
                                    OnDraw(drawingContext, stylusPoints, strokeGeometry, fillBrush); 
                                } 
                                finally
                                { 
                                    drawingContext.Close();
                                }
                                // Add it to the visual tree
                                si.StrokeRTICV.Children.Add(visual); 
                            }
 
                            return null; 
                        },
                        null); 
                    }
                }
            }
        } 

        ///////////////////////////////////////////////////////////////////// 
 
        void AbortAllStrokes()
        { 
            lock(__siLock)
            {
                while (_strokeInfoList.Count > 0)
                { 
                    TransitionStrokeVisuals(_strokeInfoList[0], true);
                } 
            } 
        }
 

        // The starting point for doing flicker free rendering when transitioning a real time
        // stroke from the DynamicRenderer thread to the application thread.
        // 
        // There's a multi-step process to do this.  We now alternate between the two host visuals
        // to do the transtion work.  Only one HostVisual can be doing a full transition at one time. 
        // When ones busy the other one reverts back to just removing the real time visual without 
        // doing the full flicker free work.
        // 
        // Here's the steps for a full transition using a Single DynamicRendererHostVisual:
        //
        // 1) [UI Thread] Set HostVisual.Clip = zero rect and then wait for render complete of that
        // 2) [UI Thread] On RenderComplete gets hit - Call over to DR thread to remove real time visual 
        // 3) [DR Thread] Removed real time stroke visual and wait for rendercomplete of that
        // 4) [DR Thread] On RenderComplete of that call back over to UI thread to let it know that's done 
        // 5) [UI Thread] Reset HostVisual.Clip = null and wait for render complete of that 
        // 6) [UI Thread] On rendercomplete - we done.  Mark this HostVisual as free.
        // 
        // In the case of another stroke coming through before a previous transition has completed
        // then basically instead of starting with step 1 we jump to step 2 and when then on step 5
        // we mark the HostVisual free and we are done.
        // 
        void TransitionStrokeVisuals(StrokeInfo si, bool abortStroke)
        { 
            // Make sure we don't get any more input for this stroke. 
            RemoveStrokeInfo(si);
 
            // remove si visuals and this si
            if (si.StrokeCV != null)
            {
                if (_mainRawInkContainerVisual != null) 
                {
                    _mainRawInkContainerVisual.Children.Remove(si.StrokeCV); 
                } 
                si.StrokeCV = null;
            } 

            si.FillBrush = null;

            // Nothing to do if we've destroyed our host visuals. 
            if (_rawInkHostVisual1 == null)
                return; 
 
            bool doRenderComplete = false;
 
            // See if we can do full transition (only when none in progress and not abort)
            if (!abortStroke && _renderCompleteStrokeInfo == null)
            {
                // make sure lock does not cause reentrancy on application thread! 
                using (_applicationDispatcher.DisableProcessing())
                { 
                    lock (__siLock) 
                    {
                        // We can transition the host visual only if a single reference is on it. 
                        if (si.StrokeHV.HasSingleReference)
                        {
                            Debug.Assert(si.StrokeHV.Clip == null);
                            si.StrokeHV.Clip = _zeroSizedFrozenRect; 
                            Debug.Assert(_renderCompleteStrokeInfo == null);
                            _renderCompleteStrokeInfo = si; 
                            doRenderComplete = true; 
                        }
                    } 
                }
            }

            if (doRenderComplete) 
            {
                NotifyOnNextRenderComplete(); 
            } 
            else
            { 
                // Just wait to dynamic rendering thread is updated then we're done.
                RemoveDynamicRendererVisualAndNotifyWhenDone(si);
            }
        } 

        // Figures out the correct DynamicRenderHostVisual to use. 
        private DynamicRendererHostVisual GetCurrentHostVisual() 
        {
            // Find which of the two host visuals to use as current. 
            if (_currentHostVisual == null)
            {
                _currentHostVisual = _rawInkHostVisual1;
            } 
            else
            { 
                HostVisual transitioningHostVisual = _renderCompleteStrokeInfo != null ? 
                                                        _renderCompleteStrokeInfo.StrokeHV : null;
 
                if (_currentHostVisual.InUse)
                {
                    if (_currentHostVisual == _rawInkHostVisual1)
                    { 
                        if (!_rawInkHostVisual2.InUse || _rawInkHostVisual1 == transitioningHostVisual)
                        { 
                            _currentHostVisual = _rawInkHostVisual2; 
                        }
                    } 
                    else
                    {
                        if (!_rawInkHostVisual1.InUse || _rawInkHostVisual2 == transitioningHostVisual)
                        { 
                            _currentHostVisual = _rawInkHostVisual1;
                        } 
                    } 
                }
            } 
            return _currentHostVisual;
        }

 
        // Removes ref from DynamicRendererHostVisual.
        void TransitionComplete(StrokeInfo si) 
        { 
            // make sure lock does not cause reentrancy on application thread!
            using(_applicationDispatcher.DisableProcessing()) 
            {
                lock(__siLock)
                {
                    si.StrokeHV.RemoveStrokeInfoRef(si); 
                }
            } 
        } 

        void RemoveStrokeInfo(StrokeInfo si) 
        {
            lock(__siLock)
            {
                _strokeInfoList.Remove(si); 
            }
        } 
 
        StrokeInfo FindStrokeInfo(int timestamp)
        { 
            lock(__siLock)
            {
                for (int i=0; i < _strokeInfoList.Count; i++)
                { 
                    StrokeInfo siCur = _strokeInfoList[i];
 
                    if (siCur.IsTimestampWithin(timestamp)) 
                    {
                        return siCur; 
                    }
                }
            }
 
            return null;
        } 
 
        /////////////////////////////////////////////////////////////////////
 
        /////////////////////////////////////////////////////////////////////
        /// 
        /// [TBS] - On UIContext
        ///  
        public DrawingAttributes DrawingAttributes
        { 
            get // called from two UIContexts 
            {
                return _drawAttrsSource; 
            }
            set // (called in UIContext)
            {
                if (value == null) 
                    throw new ArgumentNullException("value");
 
                _drawAttrsSource = value; 

                OnDrawingAttributesReplaced(); 
            }
        }

        private void CreateInkingVisuals() 
        {
            if (_mainContainerVisual == null) 
            { 
                _mainContainerVisual = new ContainerVisual();
                _mainRawInkContainerVisual = new ContainerVisual(); 
                _mainContainerVisual.Children.Add(_mainRawInkContainerVisual);
            }

            if (IsActiveForInput) 
            {
                // Make sure lock() doesn't cause reentrancy. 
                using (Element.Dispatcher.DisableProcessing()) 
                {
                    CreateRealTimeVisuals(); 
                }
            }
        }
 
        /// 
        /// Create the visual target 
        /// This method is called from the application context 
        /// 
        private void CreateRealTimeVisuals() 
        {
            // Only create if we have a root visual and have not already created them.
            if (_mainContainerVisual != null && _rawInkHostVisual1 == null)
            { 
                // Create new VisualTarget and hook up in apps visuals under element.
                _rawInkHostVisual1 = new DynamicRendererHostVisual(); 
                _rawInkHostVisual2 = new DynamicRendererHostVisual(); 
                _currentHostVisual = null;  // Pick a new current HostVisual on first stylus input.
                _mainContainerVisual.Children.Add(_rawInkHostVisual1); 
                _mainContainerVisual.Children.Add(_rawInkHostVisual2);
                // NOTE: Do the work later if perf is bad hooking up VisualTargets on StylusDown...

                // Guarentee that objects are valid when on the DR thread below. 
                //DynamicRendererHostVisual[] myArgs = new DynamicRendererHostVisual[2] { _rawInkHostVisual1, _rawInkHostVisual2 };
 
                // Do this last since we can be reentrant on this call and we want to set 
                // things up so we are all set except for the real time thread visuals which
                // we set up on first usage. 
                _renderingThread = DynamicRendererThreadManager.GetCurrentThreadInstance();

                /*
                // We are being called by the main UI thread, so invoke a call over to 
                // the inking thread to create the visual targets.
                // NOTE: Since input rendering uses the same priority we are guanenteed 
                //       that this will be processed before any input will try to be rendererd. 
                _renderingThread.ThreadDispatcher.BeginInvoke(DispatcherPriority.Send,
                (DispatcherOperationCallback)delegate(object args) 
                {
                    DynamicRendererHostVisual[] hostVisuals = (DynamicRendererHostVisual[])args;
                    VisualTarget vt;
                    // Query the VisualTarget properties to initialize them. 
                    vt = hostVisuals[0].VisualTarget;
                    vt = hostVisuals[1].VisualTarget; 
 
                    return null;
                }, 
                myArgs);
                */
            }
        } 

        ///  
        /// Unhoot the visual target. 
        /// This method is called from the application Dispatcher
        ///  
        private void DestroyRealTimeVisuals()
        {
            // Only need to handle if already created visuals.
            if (_mainContainerVisual != null && _rawInkHostVisual1 != null) 
            {
                // Make sure we unhook the rendercomplete event. 
                if (_waitingForRenderComplete) 
                {
                    MediaContext.From(_applicationDispatcher).RenderComplete -= _onRenderComplete; 
                    _waitingForRenderComplete = false;
                }

                _mainContainerVisual.Children.Remove(_rawInkHostVisual1); 
                _mainContainerVisual.Children.Remove(_rawInkHostVisual2);
 
                _renderCompleteStrokeInfo = null; 

                DynamicRendererThreadManager renderingThread = _renderingThread; // keep ref to keep it alive in this routine 
                Dispatcher drDispatcher = renderingThread != null ? renderingThread.ThreadDispatcher : null;

                if (drDispatcher != null)
                { 
                    drDispatcher.BeginInvoke(DispatcherPriority.Send,
                    (DispatcherOperationCallback)delegate(object unused) 
                    { 
                        _renderCompleteDRThreadStrokeInfoList.Clear();
 
                        drDispatcher = renderingThread.ThreadDispatcher;

                        if (drDispatcher != null && _waitingForDRThreadRenderComplete)
                        { 
                            MediaContext.From(drDispatcher).RenderComplete -= _onDRThreadRenderComplete;
                        } 
                        _waitingForDRThreadRenderComplete = false; 

                        return null; 
                    },
                    null);
                }
 
                // Make sure to free up inking thread ref to ensure thread shuts down properly.
                _renderingThread = null; 
 
                _rawInkHostVisual1 = null;
                _rawInkHostVisual2 = null; 
                _currentHostVisual = null;  // We create new HostVisuals next time we're enabled.

                AbortAllStrokes(); // Doing this here avoids doing a begininvoke to enter the rendering thread (avoid reentrancy).
            } 
        }
 
        ///////////////////////////////////////////////////////////////////// 
        private Dispatcher          _applicationDispatcher;
        private Geometry            _zeroSizedFrozenRect; 
        private DrawingAttributes   _drawAttrsSource = new DrawingAttributes();
        List            _strokeInfoList = new List();

        // Visuals layout: 
        //
        //  _mainContainerVisual (root of inking tree - RootVisual [on app Dispatcher]) 
        //     | 
        //     +-- _mainRawInkDispatcher (app dispatcher based stylus events renderer here [on app dispatcher])
        //     | 
        //     +-- _rawInkHostVisual1 (HostVisual for inking on separate thread [on app dispatcher])
        //     |          |
        //     |          +-- VisualTarget ([on RealTimeInkingDispatcher thread])
        //     | 
        //     +-- _rawInkHostVisual2 (HostVisual for inking on separate thread [on app dispatcher])
        //                | 
        //                +-- VisualTarget ([on RealTimeInkingDispatcher thread]) 
        //
        private ContainerVisual              _mainContainerVisual; 
        private ContainerVisual              _mainRawInkContainerVisual;
        private DynamicRendererHostVisual    _rawInkHostVisual1;
        private DynamicRendererHostVisual    _rawInkHostVisual2;
 
        DynamicRendererHostVisual            _currentHostVisual; // Current HV.
 
        // For OnRenderComplete support (for UI Thread) 
        EventHandler  _onRenderComplete;
        bool          _waitingForRenderComplete; 
        object        __siLock = new object();
        private StrokeInfo  _renderCompleteStrokeInfo;

        // On internal real time ink rendering thread. 
        private DynamicRendererThreadManager _renderingThread;
 
        // For OnRenderComplete support (for DynamicRenderer Thread) 
        EventHandler  _onDRThreadRenderComplete;
        bool          _waitingForDRThreadRenderComplete; 
        Queue    _renderCompleteDRThreadStrokeInfoList = new Queue();

    }
} 

// File provided for Reference Use Only by Microsoft Corporation (c) 2007.
// Copyright (c) Microsoft Corporation. All rights reserved.


                        

Link Menu

Network programming in C#, Network Programming in VB.NET, Network Programming in .NET
This book is available now!
Buy at Amazon US or
Buy at Amazon UK