Пример #1
0
        // 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);
            }
        } 
Пример #2
0
 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);
     } 
 }
Пример #3
0
        ///////////////////////////////////////////////////////////////////// 

        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();
 
                        // NTRAID#WINOS-1133229-2005/04/27-XIAOTU: two incrementally rendered stroke segments blend together 
                        // at the rendering point location, thus the alpha value at those locations are higher than the set value.
                        // This is like you draw two strokes using static rendeer and the intersection part becomes darker. 
                        // Set the opacity of the RootContainerVisual of the whole incremental stroke as color.A/255.0 and override
                        // the alpha value of the color we send to mil for rendering.
                        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();
 
                                    // NTRAID#WINOS-1133229-2005/04/27-XIAOTU: two incrementally rendered stroke segments blend together 
                                    // at the rendering point location, thus the alpha value at those locations are higher than the set value.
                                    // This is like you draw two strokes using static rendeer and the intersection part becomes darker. 
                                    // Set the opacity of the RootContainerVisual of the whole incremental stroke as color.A/255.0 and override
                                    // the alpha value of the color we send to mil for rendering.
                                    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); 
                    }
                }
            }
        } 
Пример #4
0
        /////////////////////////////////////////////////////////////////////
        /// <summary> 
        /// [TBS]
        /// </summary> 
        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);
            } 
        }
Пример #5
0
        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);
                } 
            }
        }
Пример #6
0
 internal void RemoveStrokeInfoRef(StrokeInfo si)
 { 
     _strokeInfoList.Remove(si);
 }
Пример #7
0
        /////////////////////////////////////////////////////////////////////
        /// <summary>
        /// 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. 
        /// </summary> 
        /// <param name="stylusDevice"></param>
        /// <param name="stylusPoints"></param> 
        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.
                    } 
                }
            } 
        } 
Пример #8
0
 internal void AddStrokeInfoRef(StrokeInfo si) 
 {
     _strokeInfoList.Add(si); 
 } 
Пример #9
0
 void RemoveStrokeInfo(StrokeInfo si) 
 {
     lock(__siLock)
     {
         _strokeInfoList.Remove(si); 
     }
 } 
Пример #10
0
 // 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); 
         }
     } 
 } 
Пример #11
0
        /// <summary>
        /// Check whether a certain percentage of the stroke is within the lasso
        /// </summary>
        /// <param name="lassoPoints"></param>
        /// <param name="percentageWithinLasso"></param>
        /// <returns></returns>
        public bool HitTest(IEnumerable<Point> lassoPoints, int percentageWithinLasso)
        {
            if (lassoPoints == null)
            {
                throw new System.ArgumentNullException("lassoPoints");
            }

            if ((percentageWithinLasso < 0) || (percentageWithinLasso > 100))
            {
                throw new System.ArgumentOutOfRangeException("percentageWithinLasso");
            }

            if (percentageWithinLasso == 0)
            {
                return true;
            }


            StrokeInfo strokeInfo = null;
            try
            {
                strokeInfo = new StrokeInfo(this);

                StylusPointCollection stylusPoints = strokeInfo.StylusPoints;
                double target = strokeInfo.TotalWeight * percentageWithinLasso / 100.0f - PercentageTolerance;

                Lasso lasso = new SingleLoopLasso();
                lasso.AddPoints(lassoPoints);

                for (int i = 0; i < stylusPoints.Count; i++)
                {
                    if (true == lasso.Contains((Point)stylusPoints[i]))
                    {
                        target -= strokeInfo.GetPointWeight(i);
                        if (DoubleUtil.LessThan(target, 0f))
                        {
                            return true;
                        }
                    }
                }

                return false;
            }
            finally
            {
                if (strokeInfo != null)
                {
                    //detach from event handlers, or else we leak.
                    strokeInfo.Detach();
                }
            }

        }
Пример #12
0
        /// <summary>
        /// Check whether a certain percentage of the stroke is within the Rect passed in.
        /// </summary>
        /// <param name="bounds"></param>
        /// <param name="percentageWithinBounds"></param>
        /// <returns></returns>
        public bool HitTest(Rect bounds, int percentageWithinBounds)
        {
            if ((percentageWithinBounds < 0) || (percentageWithinBounds > 100))
            {
                throw new System.ArgumentOutOfRangeException("percentageWithinBounds");
            }

            if (percentageWithinBounds == 0)
            {
                return true;
            }

            StrokeInfo strokeInfo = null;
            try
            {
                strokeInfo = new StrokeInfo(this);

                StylusPointCollection stylusPoints = strokeInfo.StylusPoints;
                double target = strokeInfo.TotalWeight * percentageWithinBounds / 100.0f - PercentageTolerance;

                for (int i = 0; i < stylusPoints.Count; i++)
                {
                    if (true == bounds.Contains((Point)stylusPoints[i]))
                    {
                        target -= strokeInfo.GetPointWeight(i);
                        if (DoubleUtil.LessThanOrClose(target, 0d))
                        {
                            return true;
                        }
                    }
                }

                return false;
            }
            finally
            {
                if (strokeInfo != null)
                {
                    //detach from event handlers, or else we leak.
                    strokeInfo.Detach();
                }
            }
        }