How to achieve ink replay without losing the original properties of the ink?

鲤鱼 秀 45 Reputation points
2023-11-05T15:18:56.97+00:00

I'm trying to implement ink replay in my application, and I'm using the code from this sample application https://github.com/microsoft/Windows-universal-samples/releases/download/MicrosoftDocs-Samples/SimpleInk.zip, and the key content is as follows.

However, the following code does not apply the original properties of the ink when playing the animation. Let's say I draw a highlighter handwriting, but when the ink is replayed, it's a black ballpoint pen handwriting.

I would appreciate it if you could help me.

private void OnReplay(object sender, RoutedEventArgs e)
        {
            if (strokeBuilder == null)
            {
                strokeBuilder = new InkStrokeBuilder();
                inkReplayTimer = new DispatcherTimer();
                inkReplayTimer.Interval = new TimeSpan(TimeSpan.TicksPerSecond / FPS);
                inkReplayTimer.Tick += InkReplayTimer_Tick;
            }
            strokesToReplay = inkCanvas.InkPresenter.StrokeContainer.GetStrokes();
            ReplayButton.IsEnabled = false;
            inkCanvas.InkPresenter.IsInputEnabled = false;
            ClearCanvasStrokeCache();
            // Calculate the beginning of the earliest stroke and the end of the latest stroke.
            // This establishes the time period during which the strokes were collected.
            beginTimeOfRecordedSession = DateTimeOffset.MaxValue;
            endTimeOfRecordedSession = DateTimeOffset.MinValue;
            foreach (InkStroke stroke in strokesToReplay)
            {
                DateTimeOffset? startTime = stroke.StrokeStartedTime;
                TimeSpan? duration = stroke.StrokeDuration;
                if (startTime.HasValue && duration.HasValue)
                {
                    if (beginTimeOfRecordedSession > startTime.Value)
                    {
                        beginTimeOfRecordedSession = startTime.Value;
                    }
                    if (endTimeOfRecordedSession < startTime.Value + duration.Value)
                    {
                        endTimeOfRecordedSession = startTime.Value + duration.Value;
                    }
                }
            }
            // If we found at least one stroke with a timestamp, then we can replay.
            if (beginTimeOfRecordedSession != DateTimeOffset.MaxValue)
            {
                durationOfRecordedSession = endTimeOfRecordedSession - beginTimeOfRecordedSession;
                ReplayProgress.Maximum = durationOfRecordedSession.TotalMilliseconds;
                ReplayProgress.Value = 0.0;
                ReplayProgress.Visibility = Visibility.Visible;
                beginTimeOfReplay = DateTime.Now;
                inkReplayTimer.Start();
                rootPage.NotifyUser("Replay started.", NotifyType.StatusMessage);
            }
            else
            {
                // There was nothing to replay. Either there were no strokes at all,
                // or none of the strokes had timestamps.
                StopReplay();
            }
        }
        private void StopReplay()
        {
            inkReplayTimer?.Stop();
            ReplayButton.IsEnabled = true;
            inkCanvas.InkPresenter.IsInputEnabled = true;
            ReplayProgress.Visibility = Visibility.Collapsed;
        }
        private void InkReplayTimer_Tick(object sender, object e)
        {
            var currentTimeOfReplay = DateTimeOffset.Now;
            TimeSpan timeElapsedInReplay = currentTimeOfReplay - beginTimeOfReplay;
            ReplayProgress.Value = timeElapsedInReplay.TotalMilliseconds;
            DateTimeOffset timeEquivalentInRecordedSession = beginTimeOfRecordedSession + timeElapsedInReplay;
            inkCanvas.InkPresenter.StrokeContainer = GetCurrentStrokesView(timeEquivalentInRecordedSession);
            if (timeElapsedInReplay > durationOfRecordedSession)
            {
                StopReplay();
                rootPage.NotifyUser("Replay finished.", NotifyType.StatusMessage);
            }
        }
        private InkStrokeContainer GetCurrentStrokesView(DateTimeOffset time)
        {
            var inkStrokeContainer = new InkStrokeContainer();
            // The purpose of this sample is to demonstrate the timestamp usage,
            // not the algorithm. (The time complexity of the code is O(N^2).)
            foreach (InkStroke stroke in strokesToReplay)
            {
                InkStroke s = GetPartialStroke(stroke, time);
                if (s != null)
                {
                    inkStrokeContainer.AddStroke(s);
                }
            }
            return inkStrokeContainer;
        }
        private InkStroke GetPartialStroke(InkStroke stroke, DateTimeOffset time)
        {
            DateTimeOffset? startTime = stroke.StrokeStartedTime;
            TimeSpan? duration = stroke.StrokeDuration;
            if (!startTime.HasValue || !duration.HasValue)
            {
                // If a stroke does not have valid timestamp, then treat it as
                // having been drawn before the playback started.
                // We must return a clone of the stroke, because a single stroke cannot
                // exist in more than one container.
                return stroke.Clone();
            }
            if (time < startTime.Value)
            {
                // Stroke has not started
                return null;
            }
            if (time >= startTime.Value + duration.Value)
            {
                // Stroke has already ended.
                // We must return a clone of the stroke, because a single stroke cannot exist in more than one container.
                return stroke.Clone();
            }
            // Stroke has started but not yet ended.
            // Create a partial stroke on the assumption that the ink points are evenly distributed in time.
            IReadOnlyList<InkPoint> points = stroke.GetInkPoints();
            var portion = (time - startTime.Value).TotalMilliseconds / duration.Value.TotalMilliseconds;
            var count = (int)((points.Count - 1) * portion) + 1;
            return strokeBuilder.CreateStrokeFromInkPoints(points.Take(count), System.Numerics.Matrix3x2.Identity, startTime, time - startTime);
        }
Universal Windows Platform (UWP)
C#
C#
An object-oriented and type-safe programming language that has its roots in the C family of languages and includes support for component-oriented programming.
10,264 questions
XAML
XAML
A language based on Extensible Markup Language (XML) that enables developers to specify a hierarchy of objects with a set of properties and logic.
765 questions
{count} votes