From 277e46030d9d00940323f5562dca0bd3a119ce55 Mon Sep 17 00:00:00 2001 From: CJKmkp <2564608840@qq.com> Date: Sat, 4 Apr 2026 22:16:37 +0800 Subject: [PATCH] =?UTF-8?q?improve:WinRT=E5=A2=A8=E8=BF=B9=E8=AF=86?= =?UTF-8?q?=E5=88=AB=E5=8F=8A=E7=AC=94=E9=94=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../MW_SimulatePressure&InkToShape.cs | 426 ++++++++++-------- 1 file changed, 245 insertions(+), 181 deletions(-) diff --git a/Ink Canvas/MainWindow_cs/MW_SimulatePressure&InkToShape.cs b/Ink Canvas/MainWindow_cs/MW_SimulatePressure&InkToShape.cs index 0b1935dc..f0220b3c 100644 --- a/Ink Canvas/MainWindow_cs/MW_SimulatePressure&InkToShape.cs +++ b/Ink Canvas/MainWindow_cs/MW_SimulatePressure&InkToShape.cs @@ -40,7 +40,7 @@ namespace Ink_Canvas /// private List circles = new List(); - /// 串行执行墨迹转形状(尤其 WinRT 异步延后),避免多笔 BeginInvoke 交错修改 newStrokes。 + /// 串行执行墨迹转形状。WinRT 必须在 Dispatcher 上延后执行:若在 StrokeCollected(UI 线程)内对 WinRT 路径同步 GetResult(),AnalyzeAsync 延续再贴回 UI 时会死锁。 private static readonly SemaphoreSlim InkToShapeSerial = new SemaphoreSlim(1, 1); /// @@ -58,6 +58,10 @@ namespace Ink_Canvas /// 每次收笔并入批次时递增;防抖 Tick 携带快照,识别过程中若又有新笔则放弃本轮替换。 private ulong _handwritingBeautifyScheduleRevision; + /// 画布笔画 → 手写纠正的识别输入(收笔时、笔锋/首段压感合成前的点集副本;替换墨迹时仍移除画布上的原笔画)。 + private readonly Dictionary _handwritingBeautifyInkInputByCanvasStroke = + new Dictionary(); + /// /// 矩形参考线的列表 /// @@ -138,6 +142,163 @@ namespace Ink_Canvas } } + private void RunStrokeCollectedPostShapeRecognitionTail(InkCanvasStrokeCollectedEventArgs e, bool wasStraightened) + { + try + { + foreach (var stylusPoint in e.Stroke.StylusPoints) + if ((stylusPoint.PressureFactor > 0.501 || stylusPoint.PressureFactor < 0.5) && + stylusPoint.PressureFactor != 0) + return; + + try + { + if (e.Stroke.StylusPoints.Count > 3) + { + var random = new Random(); + var _speed = GetPointSpeed( + e.Stroke.StylusPoints[random.Next(0, e.Stroke.StylusPoints.Count - 1)].ToPoint(), + e.Stroke.StylusPoints[random.Next(0, e.Stroke.StylusPoints.Count - 1)].ToPoint(), + e.Stroke.StylusPoints[random.Next(0, e.Stroke.StylusPoints.Count - 1)].ToPoint()); + + RandWindow.randSeed = (int)(_speed * 100000 * 1000); + } + } + catch (Exception ex) { System.Diagnostics.Debug.WriteLine(ex); } + + switch (Settings.Canvas.InkStyle) + { + case 1: + if (penType == 0) + try + { + var stylusPoints = new StylusPointCollection(); + var n = e.Stroke.StylusPoints.Count - 1; + + for (var i = 0; i <= n; i++) + { + var speed = GetPointSpeed(e.Stroke.StylusPoints[Math.Max(i - 1, 0)].ToPoint(), + e.Stroke.StylusPoints[i].ToPoint(), + e.Stroke.StylusPoints[Math.Min(i + 1, n)].ToPoint()); + var point = new StylusPoint(); + if (speed >= 0.25) + point.PressureFactor = (float)(0.5 - 0.3 * (Math.Min(speed, 1.5) - 0.3) / 1.2); + else if (speed >= 0.05) + point.PressureFactor = (float)0.5; + else + point.PressureFactor = (float)(0.5 + 0.4 * (0.05 - speed) / 0.05); + + point.X = e.Stroke.StylusPoints[i].X; + point.Y = e.Stroke.StylusPoints[i].Y; + stylusPoints.Add(point); + } + + e.Stroke.StylusPoints = stylusPoints; + } + catch (Exception ex) { System.Diagnostics.Debug.WriteLine(ex); } + + break; + case 0: + if (penType == 0) + try + { + var stylusPoints = new StylusPointCollection(); + var n = e.Stroke.StylusPoints.Count - 1; + var pressure = 0.1; + var x = 10; + if (n == 1) return; + if (n >= x) + { + for (var i = 0; i < n - x; i++) + { + var point = new StylusPoint(); + + point.PressureFactor = (float)0.5; + point.X = e.Stroke.StylusPoints[i].X; + point.Y = e.Stroke.StylusPoints[i].Y; + stylusPoints.Add(point); + } + + for (var i = n - x; i <= n; i++) + { + var point = new StylusPoint(); + + point.PressureFactor = (float)((0.5 - pressure) * (n - i) / x + pressure); + point.X = e.Stroke.StylusPoints[i].X; + point.Y = e.Stroke.StylusPoints[i].Y; + stylusPoints.Add(point); + } + } + else + { + for (var i = 0; i <= n; i++) + { + var point = new StylusPoint(); + + point.PressureFactor = (float)(0.4 * (n - i) / n + pressure); + point.X = e.Stroke.StylusPoints[i].X; + point.Y = e.Stroke.StylusPoints[i].Y; + stylusPoints.Add(point); + } + } + + e.Stroke.StylusPoints = stylusPoints; + } + catch (Exception ex) { System.Diagnostics.Debug.WriteLine(ex); } + + break; + case 3: + break; + } + } + catch (Exception ex) { System.Diagnostics.Debug.WriteLine(ex); } + + Debug.WriteLine($"墨迹平滑检查: UseAdvancedBezierSmoothing={Settings.Canvas.UseAdvancedBezierSmoothing}, wasStraightened={wasStraightened}"); + Debug.WriteLine($"异步平滑设置: UseAsyncInkSmoothing={Settings.Canvas.UseAsyncInkSmoothing}, _inkSmoothingManager={_inkSmoothingManager != null}"); + + if (Settings.Canvas.UseAdvancedBezierSmoothing && !wasStraightened) + { + try + { + Debug.WriteLine($"开始墨迹平滑处理: 原始点数={e.Stroke.StylusPoints.Count}, 直线拉直={wasStraightened}"); + + if (inkCanvas.Strokes.Contains(e.Stroke)) + { + if (Settings.Canvas.UseAsyncInkSmoothing && _inkSmoothingManager != null) + { + Debug.WriteLine("使用异步墨迹平滑"); + _ = ProcessStrokeAsync(e.Stroke); + } + else + { + var smoothedStroke = _inkSmoothingManager?.SmoothStroke(e.Stroke) ?? e.Stroke; + + if (smoothedStroke != e.Stroke) + { + SetNewBackupOfStroke(); + _currentCommitType = CommitReason.ShapeRecognition; + inkCanvas.Strokes.Remove(e.Stroke); + inkCanvas.Strokes.Add(smoothedStroke); + _currentCommitType = CommitReason.UserInput; + } + } + } + else + { + Debug.WriteLine("原始笔画不在画布中,跳过平滑处理"); + } + } + catch (Exception ex) + { + Debug.WriteLine($"高级贝塞尔曲线平滑失败: {ex.Message}"); + } + } + else if (Settings.Canvas.FitToCurve && !wasStraightened) + { + drawingAttributes.FitToCurve = true; + } + } + /// /// 处理墨水画布的笔画收集事件 /// @@ -222,6 +383,7 @@ namespace Ink_Canvas { inkCanvas.Opacity = 1; var touchPressureSimulationApplied = false; + var preBrushHandwritingPoints = CloneStylusPointCollectionForHandwritingInput(e.Stroke?.StylusPoints); if (Settings.Canvas.DisablePressure) { @@ -332,13 +494,14 @@ namespace Ink_Canvas } } + // 实时笔锋:勿依赖 DrawingAttributes.IgnorePressure。无压感触摸/鼠标等设备上,运行时仍可能为 true, + // 会导致不进入逻辑或进入后渲染仍忽略 PressureFactor;具体在 ApplyVelocityBrushTipFromSpeed 内关闭。 if (Settings.Canvas.InkStyle == 3 && !touchPressureSimulationApplied && !Settings.Canvas.DisablePressure && penType != 1 && e.Stroke?.DrawingAttributes != null && !e.Stroke.DrawingAttributes.IsHighlighter - && !e.Stroke.DrawingAttributes.IgnorePressure && e.Stroke.StylusPoints.Count >= 3) { ApplyVelocityBrushTipFromSpeed(e.Stroke); @@ -405,6 +568,9 @@ namespace Ink_Canvas if (wasStraightened && inkCanvas.Strokes.Count > 0) strokeForHandwritingBeautify = inkCanvas.Strokes[inkCanvas.Strokes.Count - 1]; + if (wasStraightened && strokeForHandwritingBeautify != null) + preBrushHandwritingPoints = CloneStylusPointCollectionForHandwritingInput(strokeForHandwritingBeautify.StylusPoints); + if (ShapeRecognitionRouter.ShouldRunShapeRecognition( Settings.InkToShape.IsInkToShapeEnabled, ShapeRecognitionRouter.FromSettingsInt(Settings.InkToShape.ShapeRecognitionEngine))) @@ -741,202 +907,56 @@ namespace Ink_Canvas } } - void InkToShapeProcess() + bool InkToShapeProcess() { var engineMode = ShapeRecognitionRouter.FromSettingsInt(Settings.InkToShape.ShapeRecognitionEngine); if (ShapeRecognitionRouter.ResolveUseWinRt(engineMode)) { + var strokeHw = strokeForHandwritingBeautify; + var preBrushHwPts = preBrushHandwritingPoints; + var wsTail = wasStraightened; Dispatcher.BeginInvoke(new Action(async () => { try { await InkToShapeProcessCoreAsync(); if (Settings.InkToShape.EnableWinRtHandwritingStrokeBeautify) - ScheduleHandwritingGlyphReplaceAfterStrokeCollected(strokeForHandwritingBeautify, isBoardBrushStroke); + ScheduleHandwritingGlyphReplaceAfterStrokeCollected(strokeHw, isBoardBrushStroke, preBrushHwPts); + RunStrokeCollectedPostShapeRecognitionTail(e, wsTail); } catch (Exception ex) { System.Diagnostics.Debug.WriteLine(ex); } }), DispatcherPriority.Normal); - return; + return true; + } + + try + { + InkToShapeProcessCoreAsync().GetAwaiter().GetResult(); + } + catch (Exception ex) + { + System.Diagnostics.Debug.WriteLine(ex); } - InkToShapeProcessCoreAsync().GetAwaiter().GetResult(); if (Settings.InkToShape.EnableWinRtHandwritingStrokeBeautify) - ScheduleHandwritingGlyphReplaceAfterStrokeCollected(strokeForHandwritingBeautify, isBoardBrushStroke); + ScheduleHandwritingGlyphReplaceAfterStrokeCollected(strokeForHandwritingBeautify, isBoardBrushStroke, preBrushHandwritingPoints); + return false; } - InkToShapeProcess(); + if (InkToShapeProcess()) + return; } else if (Settings.InkToShape.EnableWinRtHandwritingStrokeBeautify) { - ScheduleHandwritingGlyphReplaceAfterStrokeCollected(strokeForHandwritingBeautify, isBoardBrushStroke); - } - - foreach (var stylusPoint in e.Stroke.StylusPoints) - //LogHelper.WriteLogToFile(stylusPoint.PressureFactor.ToString(), LogHelper.LogType.Info); - // 检查是否是压感笔书写 - //if (stylusPoint.PressureFactor != 0.5 && stylusPoint.PressureFactor != 0) - if ((stylusPoint.PressureFactor > 0.501 || stylusPoint.PressureFactor < 0.5) && - stylusPoint.PressureFactor != 0) - return; - - try - { - if (e.Stroke.StylusPoints.Count > 3) - { - var random = new Random(); - var _speed = GetPointSpeed( - e.Stroke.StylusPoints[random.Next(0, e.Stroke.StylusPoints.Count - 1)].ToPoint(), - e.Stroke.StylusPoints[random.Next(0, e.Stroke.StylusPoints.Count - 1)].ToPoint(), - e.Stroke.StylusPoints[random.Next(0, e.Stroke.StylusPoints.Count - 1)].ToPoint()); - - RandWindow.randSeed = (int)(_speed * 100000 * 1000); - } - } - catch (Exception ex) { System.Diagnostics.Debug.WriteLine(ex); } - - switch (Settings.Canvas.InkStyle) - { - case 1: - if (penType == 0) - try - { - var stylusPoints = new StylusPointCollection(); - var n = e.Stroke.StylusPoints.Count - 1; - var s = ""; - - for (var i = 0; i <= n; i++) - { - var speed = GetPointSpeed(e.Stroke.StylusPoints[Math.Max(i - 1, 0)].ToPoint(), - e.Stroke.StylusPoints[i].ToPoint(), - e.Stroke.StylusPoints[Math.Min(i + 1, n)].ToPoint()); - s += speed + "\t"; - var point = new StylusPoint(); - if (speed >= 0.25) - point.PressureFactor = (float)(0.5 - 0.3 * (Math.Min(speed, 1.5) - 0.3) / 1.2); - else if (speed >= 0.05) - point.PressureFactor = (float)0.5; - else - point.PressureFactor = (float)(0.5 + 0.4 * (0.05 - speed) / 0.05); - - point.X = e.Stroke.StylusPoints[i].X; - point.Y = e.Stroke.StylusPoints[i].Y; - stylusPoints.Add(point); - } - - e.Stroke.StylusPoints = stylusPoints; - } - catch (Exception ex) { System.Diagnostics.Debug.WriteLine(ex); } - - break; - case 0: - if (penType == 0) - try - { - var stylusPoints = new StylusPointCollection(); - var n = e.Stroke.StylusPoints.Count - 1; - var pressure = 0.1; - var x = 10; - if (n == 1) return; - if (n >= x) - { - for (var i = 0; i < n - x; i++) - { - var point = new StylusPoint(); - - point.PressureFactor = (float)0.5; - point.X = e.Stroke.StylusPoints[i].X; - point.Y = e.Stroke.StylusPoints[i].Y; - stylusPoints.Add(point); - } - - for (var i = n - x; i <= n; i++) - { - var point = new StylusPoint(); - - point.PressureFactor = (float)((0.5 - pressure) * (n - i) / x + pressure); - point.X = e.Stroke.StylusPoints[i].X; - point.Y = e.Stroke.StylusPoints[i].Y; - stylusPoints.Add(point); - } - } - else - { - for (var i = 0; i <= n; i++) - { - var point = new StylusPoint(); - - point.PressureFactor = (float)(0.4 * (n - i) / n + pressure); - point.X = e.Stroke.StylusPoints[i].X; - point.Y = e.Stroke.StylusPoints[i].Y; - stylusPoints.Add(point); - } - } - - e.Stroke.StylusPoints = stylusPoints; - } - catch (Exception ex) { System.Diagnostics.Debug.WriteLine(ex); } - - break; - case 3: - break; + ScheduleHandwritingGlyphReplaceAfterStrokeCollected(strokeForHandwritingBeautify, isBoardBrushStroke, preBrushHandwritingPoints); } } catch (Exception ex) { System.Diagnostics.Debug.WriteLine(ex); } - // 应用高级贝塞尔曲线平滑(仅在未进行直线拉直时) - Debug.WriteLine($"墨迹平滑检查: UseAdvancedBezierSmoothing={Settings.Canvas.UseAdvancedBezierSmoothing}, wasStraightened={wasStraightened}"); - Debug.WriteLine($"异步平滑设置: UseAsyncInkSmoothing={Settings.Canvas.UseAsyncInkSmoothing}, _inkSmoothingManager={_inkSmoothingManager != null}"); - - if (Settings.Canvas.UseAdvancedBezierSmoothing && !wasStraightened) - { - try - { - Debug.WriteLine($"开始墨迹平滑处理: 原始点数={e.Stroke.StylusPoints.Count}, 直线拉直={wasStraightened}"); - - // 检查原始笔画是否仍然存在于画布中 - if (inkCanvas.Strokes.Contains(e.Stroke)) - { - // 使用新的异步墨迹平滑管理器 - if (Settings.Canvas.UseAsyncInkSmoothing && _inkSmoothingManager != null) - { - Debug.WriteLine("使用异步墨迹平滑"); - // 异步处理 - _ = ProcessStrokeAsync(e.Stroke); - } - else - { - // 同步处理(向后兼容) - var smoothedStroke = _inkSmoothingManager?.SmoothStroke(e.Stroke) ?? e.Stroke; - - if (smoothedStroke != e.Stroke) - { - // 替换原始笔画 - SetNewBackupOfStroke(); - _currentCommitType = CommitReason.ShapeRecognition; - inkCanvas.Strokes.Remove(e.Stroke); - inkCanvas.Strokes.Add(smoothedStroke); - _currentCommitType = CommitReason.UserInput; - } - } - } - else - { - Debug.WriteLine("原始笔画不在画布中,跳过平滑处理"); - } - } - catch (Exception ex) - { - // 如果高级平滑失败,回退到原始笔画 - Debug.WriteLine($"高级贝塞尔曲线平滑失败: {ex.Message}"); - } - } - else if (Settings.Canvas.FitToCurve && !wasStraightened) - { - drawingAttributes.FitToCurve = true; - } + RunStrokeCollectedPostShapeRecognitionTail(e, wasStraightened); } /// @@ -2106,6 +2126,7 @@ namespace Ink_Canvas /// /// 将沿线速度映射为压感并与硬件压感混合,快写略细、慢写略粗;与 Inkeys 中 RTSSpeed 驱动的笔锋类似,在落笔后统一施加。 + /// 无压感设备上系统可能将 置为 true,此处强制关闭以便粗细随合成压感变化(与「屏蔽压感」无关:调用方已保证未屏蔽)。 /// private void ApplyVelocityBrushTipFromSpeed(Stroke stroke) { @@ -2115,6 +2136,9 @@ namespace Ink_Canvas if (mix <= 0 || stroke == null) return; if (mix > 1) mix = 1; + if (stroke.DrawingAttributes != null) + stroke.DrawingAttributes.IgnorePressure = false; + var pts = stroke.StylusPoints; if (pts.Count < 3) return; @@ -2700,8 +2724,12 @@ namespace Ink_Canvas { for (var i = _handwritingRecentStrokesForBeautify.Count - 1; i >= 0; i--) { - if (!inkCanvas.Strokes.Contains(_handwritingRecentStrokesForBeautify[i])) + var s = _handwritingRecentStrokesForBeautify[i]; + if (!inkCanvas.Strokes.Contains(s)) + { + _handwritingBeautifyInkInputByCanvasStroke.Remove(s); _handwritingRecentStrokesForBeautify.RemoveAt(i); + } } } @@ -2716,8 +2744,23 @@ namespace Ink_Canvas _handwritingBeautifyDebounceTimer.Tick += HandwritingBeautifyDebounceTimer_Tick; } + /// 深拷贝点集,供手写纠正识别输入(与笔锋/二次压感合成前的画布数据一致)。 + private static StylusPointCollection CloneStylusPointCollectionForHandwritingInput(StylusPointCollection source) + { + if (source == null || source.Count == 0) + return null; + var copy = new StylusPointCollection(); + foreach (StylusPoint p in source) + copy.Add(new StylusPoint(p.X, p.Y, p.PressureFactor)); + return copy; + } + /// 并入批次并重置 1s 计时器(多笔需停笔满延迟后才矫正)。 - private void ScheduleHandwritingGlyphReplaceAfterStrokeCollected(Stroke strokeForBeautify, bool isBoardBrushStroke) + /// 笔锋与后续 InkStyle 压感合成前的点集;为 null 时识别输入与画布笔画一致(兼容旧行为)。 + private void ScheduleHandwritingGlyphReplaceAfterStrokeCollected( + Stroke strokeForBeautify, + bool isBoardBrushStroke, + StylusPointCollection preBrushHandwritingPoints = null) { if (!Settings.InkToShape.EnableWinRtHandwritingStrokeBeautify) return; @@ -2730,6 +2773,18 @@ namespace Ink_Canvas _handwritingBeautifyScheduleRevision++; + if (preBrushHandwritingPoints != null && preBrushHandwritingPoints.Count > 0) + { + _handwritingBeautifyInkInputByCanvasStroke[strokeForBeautify] = new Stroke(preBrushHandwritingPoints) + { + DrawingAttributes = strokeForBeautify.DrawingAttributes.Clone() + }; + } + else + { + _handwritingBeautifyInkInputByCanvasStroke.Remove(strokeForBeautify); + } + var alreadyInBatch = false; foreach (Stroke x in _handwritingRecentStrokesForBeautify) { @@ -2780,30 +2835,36 @@ namespace Ink_Canvas PruneHandwritingBeautifyBatch(); - var input = new StrokeCollection(); + var canvasStrokes = new StrokeCollection(); + var recognitionInput = new StrokeCollection(); foreach (Stroke s in _handwritingRecentStrokesForBeautify) { - if (inkCanvas.Strokes.Contains(s)) - input.Add(s); + if (!inkCanvas.Strokes.Contains(s)) + continue; + canvasStrokes.Add(s); + if (_handwritingBeautifyInkInputByCanvasStroke.TryGetValue(s, out var inkInput) && inkInput != null) + recognitionInput.Add(inkInput); + else + recognitionInput.Add(s); } - if (input.Count == 0) + if (canvasStrokes.Count == 0) return; if (_handwritingBeautifyScheduleRevision != revisionWhenIdle) return; var shapeMode = ShapeRecognitionRouter.FromSettingsInt(Settings.InkToShape.ShapeRecognitionEngine); - var result = await InkRecognizeHelper.CorrectHandwritingStrokesUnifiedAsync(input, shapeMode).ConfigureAwait(true); + var result = await InkRecognizeHelper.CorrectHandwritingStrokesUnifiedAsync(recognitionInput, shapeMode).ConfigureAwait(true); if (_handwritingBeautifyScheduleRevision != revisionWhenIdle) return; if (result == null || result.Count == 0) return; - if (ReferenceEquals(result, input)) + if (ReferenceEquals(result, recognitionInput)) return; var anyInputStillPresent = false; - foreach (Stroke s in input) + foreach (Stroke s in canvasStrokes) { if (inkCanvas.Strokes.Contains(s)) { @@ -2817,7 +2878,7 @@ namespace Ink_Canvas SetNewBackupOfStroke(); _currentCommitType = CommitReason.ShapeRecognition; - foreach (Stroke s in input) + foreach (Stroke s in canvasStrokes) { if (inkCanvas.Strokes.Contains(s)) inkCanvas.Strokes.Remove(s); @@ -2827,8 +2888,11 @@ namespace Ink_Canvas inkCanvas.Strokes.Add(s); _currentCommitType = CommitReason.UserInput; - foreach (Stroke s in input) + foreach (Stroke s in canvasStrokes) + { _handwritingRecentStrokesForBeautify.Remove(s); + _handwritingBeautifyInkInputByCanvasStroke.Remove(s); + } PruneHandwritingBeautifyBatch(); }