How to achieve ink replay without losing the original properties of the ink?
鲤鱼 秀
45
Reputation points
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);
}