From ebbe018baed9eb8cb4c1955a482a34bf0702a4dc Mon Sep 17 00:00:00 2001 From: CJKmkp <2564608840@qq.com> Date: Sun, 5 Apr 2026 18:52:19 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BB=A3=E7=A0=81=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Ink Canvas/App.xaml | 5 +- Ink Canvas/Helpers/InkRecognizeHelper.cs | 8 +- Ink Canvas/Helpers/SoftwareLauncher.cs | 15 +- Ink Canvas/Helpers/TimeMachine.cs | 8 +- .../Helpers/WinRtHandwritingRecognizer.cs | 11 +- Ink Canvas/Helpers/WinRtInkShapeRecognizer.cs | 20 ++- .../MW_SimulatePressure&InkToShape.cs | 156 ++++++++++-------- Ink Canvas/MainWindow_cs/MW_TouchEvents.cs | 4 +- Ink Canvas/Properties/Strings.Designer.cs | 2 + Ink Canvas/Properties/Strings.enUS.xml | 2 + Ink Canvas/Properties/Strings.resx | 8 + 11 files changed, 147 insertions(+), 92 deletions(-) diff --git a/Ink Canvas/App.xaml b/Ink Canvas/App.xaml index 3d4723fd..df03e568 100644 --- a/Ink Canvas/App.xaml +++ b/Ink Canvas/App.xaml @@ -2,6 +2,7 @@ xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="clr-namespace:Ink_Canvas" + xmlns:props="clr-namespace:Ink_Canvas.Properties" xmlns:tb="clr-namespace:H.NotifyIcon;assembly=H.NotifyIcon.Wpf" xmlns:ui="http://schemas.inkore.net/lib/ui/wpf/modern" xmlns:ikw="http://schemas.inkore.net/lib/ui/wpf" @@ -35,7 +36,7 @@ - + @@ -55,7 +56,7 @@ - + diff --git a/Ink Canvas/Helpers/InkRecognizeHelper.cs b/Ink Canvas/Helpers/InkRecognizeHelper.cs index 5855fd9f..d283aaa5 100644 --- a/Ink Canvas/Helpers/InkRecognizeHelper.cs +++ b/Ink Canvas/Helpers/InkRecognizeHelper.cs @@ -48,13 +48,17 @@ namespace Ink_Canvas.Helpers break; } if (alternates.Count > 0) - analysisAlternate = alternates[0]; + { + var altFinal = alternates[0]; + if (altFinal?.AlternateNodes != null && altFinal.AlternateNodes.Count > 0) + analysisAlternate = altFinal; + } } } analyzer.Dispose(); - if (analysisAlternate != null && analysisAlternate.AlternateNodes.Count > 0) + if (analysisAlternate != null && analysisAlternate.AlternateNodes != null && analysisAlternate.AlternateNodes.Count > 0) { var node = analysisAlternate.AlternateNodes[0] as InkDrawingNode; if (node == null) diff --git a/Ink Canvas/Helpers/SoftwareLauncher.cs b/Ink Canvas/Helpers/SoftwareLauncher.cs index 395eadf3..69109e84 100644 --- a/Ink Canvas/Helpers/SoftwareLauncher.cs +++ b/Ink Canvas/Helpers/SoftwareLauncher.cs @@ -49,16 +49,13 @@ namespace Ink_Canvas.Helpers if (string.IsNullOrWhiteSpace(softwareName)) return null; - // 64 位进程默认只枚举 64 位注册表视图;32 位希沃常写在 WOW6432Node 下,需一并扫描。 - string[] uninstallRoots = + // 须用 OpenBaseKey + RegistryView 显式指定视图:Registry.LocalMachine.OpenSubKey 跟随进程位数, + // 32 位进程下无法靠拼接 WOW6432Node 路径进入 64 位视图,会找不到 64 位安装的展台。 + const string uninstallSubKey = @"SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall"; + foreach (RegistryView view in new[] { RegistryView.Registry64, RegistryView.Registry32 }) { - @"SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall", - @"SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall", - }; - - foreach (string root in uninstallRoots) - { - using (RegistryKey key = Registry.LocalMachine.OpenSubKey(root)) + using (RegistryKey baseKey = RegistryKey.OpenBaseKey(RegistryHive.LocalMachine, view)) + using (RegistryKey key = baseKey.OpenSubKey(uninstallSubKey)) { if (key == null) continue; string found = FindInUninstallKey(key, softwareName); diff --git a/Ink Canvas/Helpers/TimeMachine.cs b/Ink Canvas/Helpers/TimeMachine.cs index 492c96cc..1d86caf7 100644 --- a/Ink Canvas/Helpers/TimeMachine.cs +++ b/Ink Canvas/Helpers/TimeMachine.cs @@ -100,8 +100,8 @@ namespace Ink_Canvas.Helpers var item = _currentStrokeHistory[_currentIndex]; item.StrokeHasBeenCleared = !item.StrokeHasBeenCleared; _currentIndex--; - OnUndoStateChanged?.Invoke(_currentIndex > -1); - OnRedoStateChanged?.Invoke(_currentStrokeHistory.Count - _currentIndex - 1 > 0); + OnUndoStateChanged?.Invoke(CanUndo); + OnRedoStateChanged?.Invoke(CanRedo); return item; } @@ -137,8 +137,8 @@ namespace Ink_Canvas.Helpers } private void NotifyUndoRedoState() { - OnUndoStateChanged?.Invoke(_currentIndex > -1); - OnRedoStateChanged?.Invoke(_currentStrokeHistory.Count - _currentIndex - 1 > 0); + OnUndoStateChanged?.Invoke(CanUndo); + OnRedoStateChanged?.Invoke(CanRedo); } /// 当前历史是否允许撤销。 diff --git a/Ink Canvas/Helpers/WinRtHandwritingRecognizer.cs b/Ink Canvas/Helpers/WinRtHandwritingRecognizer.cs index 8fed3e0b..ff6c06eb 100644 --- a/Ink Canvas/Helpers/WinRtHandwritingRecognizer.cs +++ b/Ink Canvas/Helpers/WinRtHandwritingRecognizer.cs @@ -40,7 +40,9 @@ namespace Ink_Canvas.Helpers { try { - await RecognizeHandwritingAsync(new StrokeCollection()).ConfigureAwait(true); + await RecognizeHandwritingAsync( + WinRtInkShapeRecognizer.CreateMinimalWarmupStrokeCollection(), + verboseTrace: false).ConfigureAwait(true); } catch { @@ -59,12 +61,15 @@ namespace Ink_Canvas.Helpers /// 再对每一分词用 GetTextCandidates(与当前 SDK 中部分版本的 /// 未暴露笔画映射的局限兼容)。 /// - public static async Task RecognizeHandwritingAsync(StrokeCollection strokes) + /// 为 false 时跳过详细识别日志(用于 等)。 + public static async Task RecognizeHandwritingAsync( + StrokeCollection strokes, + bool verboseTrace = true) { if (!IsApiAvailable || strokes == null || strokes.Count == 0) return HandwritingRecognitionResult.Empty; - var traceRecognition = strokes.Count > 0; + var traceRecognition = verboseTrace; try { diff --git a/Ink Canvas/Helpers/WinRtInkShapeRecognizer.cs b/Ink Canvas/Helpers/WinRtInkShapeRecognizer.cs index bfd0618f..c39a3621 100644 --- a/Ink Canvas/Helpers/WinRtInkShapeRecognizer.cs +++ b/Ink Canvas/Helpers/WinRtInkShapeRecognizer.cs @@ -28,7 +28,8 @@ namespace Ink_Canvas.Helpers { try { - await RecognizeShapeAsync(new StrokeCollection()); + // 空 StrokeCollection 在 RecognizeShapeAsync 入口会直接返回,无法预热 WinRT InkAnalyzer。 + await RecognizeShapeAsync(CreateMinimalWarmupStrokeCollection()).ConfigureAwait(true); } catch { @@ -99,6 +100,23 @@ namespace Ink_Canvas.Helpers } } + /// + /// 极短合成笔画,供 等场景走完整 WinRT 转换与分析管线(空集合在入口处会被直接返回)。 + /// + internal static StrokeCollection CreateMinimalWarmupStrokeCollection() + { + var da = new DrawingAttributes { Color = Colors.Black, Width = 2, Height = 2 }; + var pts = new StylusPointCollection + { + new StylusPoint(8, 8), + new StylusPoint(14, 10), + new StylusPoint(20, 8), + }; + var col = new StrokeCollection(); + col.Add(new Stroke(pts, da)); + return col; + } + /// 供 WinRT 手写等模块复用:将 WPF 转为 WinRT internal static global::Windows.UI.Input.Inking.InkStroke CreateInkStrokeFromWpf(Stroke stroke) { diff --git a/Ink Canvas/MainWindow_cs/MW_SimulatePressure&InkToShape.cs b/Ink Canvas/MainWindow_cs/MW_SimulatePressure&InkToShape.cs index fcb4fd1a..eb85e37f 100644 --- a/Ink Canvas/MainWindow_cs/MW_SimulatePressure&InkToShape.cs +++ b/Ink Canvas/MainWindow_cs/MW_SimulatePressure&InkToShape.cs @@ -176,85 +176,89 @@ namespace Ink_Canvas } catch (Exception ex) { System.Diagnostics.Debug.WriteLine(ex); } - switch (Settings.Canvas.InkStyle) + // 「屏蔽压感」已在收笔主路径将点集归一成 0.5;此处若再跑 InkStyle 0/1 会重写 PressureFactor,造成假压感。 + if (!Settings.Canvas.DisablePressure) { - case 1: - if (penType == 0) - try - { - var stylusPoints = new StylusPointCollection(); - var n = e.Stroke.StylusPoints.Count - 1; - - for (var i = 0; i <= n; i++) + switch (Settings.Canvas.InkStyle) + { + case 1: + if (penType == 0) + try { - 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 - { - PressureFactor = RateBasedPressureFactorFromPointSpeed(speed), - X = e.Stroke.StylusPoints[i].X, - Y = e.Stroke.StylusPoints[i].Y - }; - stylusPoints.Add(point); - } + var stylusPoints = new StylusPointCollection(); + var n = e.Stroke.StylusPoints.Count - 1; - 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 e.Stroke; - 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; + 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 + { + PressureFactor = RateBasedPressureFactorFromPointSpeed(speed), + X = e.Stroke.StylusPoints[i].X, + Y = e.Stroke.StylusPoints[i].Y + }; stylusPoints.Add(point); } + + e.Stroke.StylusPoints = stylusPoints; } + catch (Exception ex) { System.Diagnostics.Debug.WriteLine(ex); } - 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 e.Stroke; + if (n >= x) + { + for (var i = 0; i < n - x; i++) + { + var point = new StylusPoint(); - break; - case 3: - break; + 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); } @@ -507,7 +511,11 @@ namespace Ink_Canvas // 实时笔锋:勿依赖 DrawingAttributes.IgnorePressure。无压感触摸/鼠标等设备上,运行时仍可能为 true, // 会导致不进入逻辑或进入后渲染仍忽略 PressureFactor;具体在 ApplyVelocityBrushTipFromSpeed 内关闭。 + // 「屏蔽压感」时必须跳过:否则会重写 PressureFactor 并强制 IgnorePressure=false,与归一压感冲突。 + // VelocityBrushTipMix <= 0 时 ApplyVelocityBrushTipFromSpeed 为空操作,无需调用。 if (Settings.Canvas.InkStyle == 3 + && Settings.Canvas.VelocityBrushTipMix > 0 + && !Settings.Canvas.DisablePressure && !touchPressureSimulationApplied && penType != 1 && e.Stroke?.DrawingAttributes != null @@ -2183,12 +2191,16 @@ namespace Ink_Canvas /// /// 将沿线速度映射为压感并与硬件压感混合,快写略细、慢写略粗;在落笔时(及手写笔移动时由调用方)统一施加。 - /// 无压感设备上系统可能将 置为 true,此处强制关闭以便粗细随合成压感变化(与「屏蔽压感」无关:调用方已保证未屏蔽)。 + /// 无压感设备上系统可能将 置为 true,此处强制关闭以便粗细随合成压感变化。 + /// 若 为 true,本方法直接返回且不修改 IgnorePressure。 /// private void ApplyVelocityBrushTipFromSpeed(Stroke stroke) { try { + if (Settings.Canvas.DisablePressure) + return; + var mix = Settings.Canvas.VelocityBrushTipMix; if (mix <= 0 || stroke == null) return; if (mix > 1) mix = 1; @@ -2885,7 +2897,11 @@ namespace Ink_Canvas PruneHandwritingBeautifyBatch(); while (_handwritingRecentStrokesForBeautify.Count > HandwritingBeautifyMaxRecentStrokes) + { + var evicted = _handwritingRecentStrokesForBeautify[0]; _handwritingRecentStrokesForBeautify.RemoveAt(0); + _handwritingBeautifyInkInputByCanvasStroke.Remove(evicted); + } EnsureHandwritingBeautifyDebounceTimer(); _handwritingBeautifyDebounceTimer.Stop(); diff --git a/Ink Canvas/MainWindow_cs/MW_TouchEvents.cs b/Ink Canvas/MainWindow_cs/MW_TouchEvents.cs index 7f44331e..1b399479 100644 --- a/Ink Canvas/MainWindow_cs/MW_TouchEvents.cs +++ b/Ink Canvas/MainWindow_cs/MW_TouchEvents.cs @@ -543,10 +543,12 @@ namespace Ink_Canvas foreach (var stylusPoint in stylusPointCollection) strokeVisual.Add(new StylusPoint(stylusPoint.X, stylusPoint.Y, stylusPoint.PressureFactor)); - // 实时笔锋:在绘制过程中更新压感并整笔重绘预览;否则预览层固定线宽,收笔后改点集也看不到笔锋变化。 + // 实时笔锋:混合度 > 0 时在绘制过程中更新压感并整笔重绘预览;混合为 0 时与普通过程一致用增量 Redraw,避免每点 ForceRedraw 整笔清空(长笔画卡顿)。 var committedStroke = strokeVisual.Stroke; if (committedStroke != null && Settings.Canvas.InkStyle == 3 + && Settings.Canvas.VelocityBrushTipMix > 0 + && !Settings.Canvas.DisablePressure && penType == 0 && committedStroke.DrawingAttributes != null && !committedStroke.DrawingAttributes.IsHighlighter diff --git a/Ink Canvas/Properties/Strings.Designer.cs b/Ink Canvas/Properties/Strings.Designer.cs index ee51ee34..82df0f2b 100644 --- a/Ink Canvas/Properties/Strings.Designer.cs +++ b/Ink Canvas/Properties/Strings.Designer.cs @@ -119,5 +119,7 @@ namespace Ink_Canvas.Properties public static string Nav_About => GetString(nameof(Nav_About)) ?? "关于"; public static string App_Title => GetString(nameof(App_Title)) ?? "InkCanvasforClass"; public static string Booth_Resolution_Tooltip => GetString(nameof(Booth_Resolution_Tooltip)) ?? "展台/截图分辨率"; + public static string Tray_TempShowMainWindow => GetString(nameof(Tray_TempShowMainWindow)) ?? "显示主窗口(2分钟)"; + public static string Tray_OpenSettings => GetString(nameof(Tray_OpenSettings)) ?? "打开设置"; } } diff --git a/Ink Canvas/Properties/Strings.enUS.xml b/Ink Canvas/Properties/Strings.enUS.xml index 254d30e8..0937e4fa 100644 --- a/Ink Canvas/Properties/Strings.enUS.xml +++ b/Ink Canvas/Properties/Strings.enUS.xml @@ -763,4 +763,6 @@ Two-finger rotate Gesture Gesture options + Show main window (2 minutes) + Open settings diff --git a/Ink Canvas/Properties/Strings.resx b/Ink Canvas/Properties/Strings.resx index 03e02977..84edd0ff 100644 --- a/Ink Canvas/Properties/Strings.resx +++ b/Ink Canvas/Properties/Strings.resx @@ -782,4 +782,12 @@ 双指旋转 手势 手势选项 + + 显示主窗口(2分钟) + 系统托盘 - 临时显示主窗口 + + + 打开设置 + 系统托盘 - 打开设置 +