diff --git a/Ink Canvas/AssemblyInfo.cs b/Ink Canvas/AssemblyInfo.cs index 46331e15..67fa9e01 100644 --- a/Ink Canvas/AssemblyInfo.cs +++ b/Ink Canvas/AssemblyInfo.cs @@ -44,5 +44,5 @@ using System.Windows; // You can specify all the values or you can default the Build and Revision Numbers // by using the '*' as shown below: // [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("1.7.18.9")] -[assembly: AssemblyFileVersion("1.7.18.9")] +[assembly: AssemblyVersion("1.7.18.10")] +[assembly: AssemblyFileVersion("1.7.18.10")] diff --git a/Ink Canvas/Helpers/InkRecognizeHelper.cs b/Ink Canvas/Helpers/InkRecognizeHelper.cs index d283aaa5..cb527b78 100644 --- a/Ink Canvas/Helpers/InkRecognizeHelper.cs +++ b/Ink Canvas/Helpers/InkRecognizeHelper.cs @@ -152,6 +152,9 @@ namespace Ink_Canvas.Helpers var node = legacy.InkDrawingNode; var shape = node.GetShape(); + if (shape == null) + return InkShapeRecognitionResult.Empty; + var hot = ClonePointCollection(node.HotPoints); return new InkShapeRecognitionResult( node.GetShapeName(), @@ -173,6 +176,9 @@ namespace Ink_Canvas.Helpers public static bool IsContainShapeType(string name) { + if (string.IsNullOrEmpty(name)) + return false; + if (name.Contains("Triangle") || name.Contains("Circle") || name.Contains("Rectangle") || name.Contains("Diamond") || name.Contains("Parallelogram") || name.Contains("Square") diff --git a/Ink Canvas/Helpers/WinRtHandwritingRecognizer.cs b/Ink Canvas/Helpers/WinRtHandwritingRecognizer.cs index ff6c06eb..6b90f765 100644 --- a/Ink Canvas/Helpers/WinRtHandwritingRecognizer.cs +++ b/Ink Canvas/Helpers/WinRtHandwritingRecognizer.cs @@ -29,31 +29,13 @@ namespace Ink_Canvas.Helpers public static bool IsApiAvailable => OSVersion.GetOperatingSystem() >= OSVersionExtension.OperatingSystem.Windows10; + /// + /// 启动阶段不再预热线程内 WinRT 手写管线。历史上曾用 跑全链路, + /// 会显著拖慢启动;与更早的「空 」一样,此处不再在 Idle 上做任何工作。 + /// 首次真正需要手写识别时由 承担冷启动成本。 + /// public static void Warmup() { - if (!IsApiAvailable || !Environment.Is64BitProcess) return; - try - { - var d = Application.Current?.Dispatcher; - if (d == null) return; - d.BeginInvoke(new Action(async () => - { - try - { - await RecognizeHandwritingAsync( - WinRtInkShapeRecognizer.CreateMinimalWarmupStrokeCollection(), - verboseTrace: false).ConfigureAwait(true); - } - catch - { - // ignore - } - })); - } - catch - { - // ignore - } } /// diff --git a/Ink Canvas/Helpers/WinRtInkShapeRecognizer.cs b/Ink Canvas/Helpers/WinRtInkShapeRecognizer.cs index c39a3621..687d6980 100644 --- a/Ink Canvas/Helpers/WinRtInkShapeRecognizer.cs +++ b/Ink Canvas/Helpers/WinRtInkShapeRecognizer.cs @@ -124,6 +124,9 @@ namespace Ink_Canvas.Helpers return null; var da = stroke.DrawingAttributes; + if (da == null) + return null; + var wda = new global::Windows.UI.Input.Inking.InkDrawingAttributes { PenTip = global::Windows.UI.Input.Inking.PenTipShape.Circle, diff --git a/Ink Canvas/MainWindow.xaml b/Ink Canvas/MainWindow.xaml index 33af03f8..3c3b8c40 100644 --- a/Ink Canvas/MainWindow.xaml +++ b/Ink Canvas/MainWindow.xaml @@ -1487,6 +1487,7 @@ + diff --git a/Ink Canvas/MainWindow.xaml.cs b/Ink Canvas/MainWindow.xaml.cs index 378c3495..490b7f39 100644 --- a/Ink Canvas/MainWindow.xaml.cs +++ b/Ink Canvas/MainWindow.xaml.cs @@ -3733,6 +3733,9 @@ namespace Ink_Canvas { try { + if (!IsLoaded) + return; + if (Settings.ModeSettings.IsPPTOnlyMode) { if (TrayTemporaryShowUntilUtc.HasValue && DateTime.UtcNow < TrayTemporaryShowUntilUtc.Value) diff --git a/Ink Canvas/MainWindow_cs/MW_Settings.cs b/Ink Canvas/MainWindow_cs/MW_Settings.cs index 4a85efe8..dba3a0c0 100644 --- a/Ink Canvas/MainWindow_cs/MW_Settings.cs +++ b/Ink Canvas/MainWindow_cs/MW_Settings.cs @@ -1887,54 +1887,22 @@ namespace Ink_Canvas - // 注释掉这些方法,因为对应的UI控件还没有在XAML中定义 - /* - private void ToggleSwitchAsyncInkSmoothing_Toggled(object sender, RoutedEventArgs e) { + private void ToggleSwitchDisableHardwareAcceleration_Toggled(object sender, RoutedEventArgs e) + { if (!isLoaded) return; - Settings.Canvas.UseAsyncInkSmoothing = ToggleSwitchAsyncInkSmoothing.IsOn; - _inkSmoothingManager?.UpdateConfig(); - SaveSettingsToFile(); - } - - private void ToggleSwitchHardwareAcceleration_Toggled(object sender, RoutedEventArgs e) { - if (!isLoaded) return; - Settings.Canvas.UseHardwareAcceleration = ToggleSwitchHardwareAcceleration.IsOn; - _inkSmoothingManager?.UpdateConfig(); - SaveSettingsToFile(); - } - - private void ComboBoxInkSmoothingQuality_SelectionChanged(object sender, SelectionChangedEventArgs e) { - if (!isLoaded) return; - Settings.Canvas.InkSmoothingQuality = ComboBoxInkSmoothingQuality.SelectedIndex; - _inkSmoothingManager?.UpdateConfig(); - SaveSettingsToFile(); - } - - private void SliderMaxConcurrentTasks_ValueChanged(object sender, RoutedPropertyChangedEventArgs e) { - if (!isLoaded) return; - Settings.Canvas.MaxConcurrentSmoothingTasks = (int)SliderMaxConcurrentTasks.Value; - _inkSmoothingManager?.UpdateConfig(); - SaveSettingsToFile(); - } - - private void ButtonApplyRecommendedSettings_Click(object sender, RoutedEventArgs e) { - // 应用推荐的性能设置 - Helpers.InkSmoothingManager.ApplyRecommendedSettings(); - LoadSettings(false); - _inkSmoothingManager?.UpdateConfig(); - SaveSettingsToFile(); - - ShowNotification("已应用推荐的性能设置"); - } - - private void ButtonShowPerformanceStats_Click(object sender, RoutedEventArgs e) { - if (_inkSmoothingManager != null) + var isOnObj = sender?.GetType().GetProperty("IsOn")?.GetValue(sender); + if (isOnObj is bool isOn) { - var stats = _inkSmoothingManager.GetPerformanceStats(); - ShowNotification($"性能统计: {stats}"); + Settings.Canvas.UseHardwareAcceleration = !isOn; } + else + { + return; + } + + _inkSmoothingManager?.UpdateConfig(); + SaveSettingsToFile(); } - */ private void ToggleSwitchAutoSaveStrokesInPowerPoint_Toggled(object sender, RoutedEventArgs e) { diff --git a/Ink Canvas/MainWindow_cs/MW_SettingsToLoad.cs b/Ink Canvas/MainWindow_cs/MW_SettingsToLoad.cs index 40a74c89..9f06df49 100644 --- a/Ink Canvas/MainWindow_cs/MW_SettingsToLoad.cs +++ b/Ink Canvas/MainWindow_cs/MW_SettingsToLoad.cs @@ -699,22 +699,7 @@ namespace Ink_Canvas drawingAttributes.FitToCurve = false; } - // 注释掉新的墨迹平滑性能设置,因为UI控件还没有定义 - /* - // 初始化新的墨迹平滑性能设置 - ToggleSwitchAsyncInkSmoothing.IsOn = Settings.Canvas.UseAsyncInkSmoothing; - ToggleSwitchHardwareAcceleration.IsOn = Settings.Canvas.UseHardwareAcceleration; - ComboBoxInkSmoothingQuality.SelectedIndex = Settings.Canvas.InkSmoothingQuality; - SliderMaxConcurrentTasks.Value = Settings.Canvas.MaxConcurrentSmoothingTasks > 0 ? - Settings.Canvas.MaxConcurrentSmoothingTasks : Environment.ProcessorCount; - - // 检查硬件加速支持 - if (!Helpers.InkSmoothingManager.IsHardwareAccelerationSupported()) - { - ToggleSwitchHardwareAcceleration.IsEnabled = false; - // 可以添加提示文本说明硬件加速不可用 - } - */ + ToggleSwitchDisableHardwareAcceleration.IsOn = !Settings.Canvas.UseHardwareAcceleration; // 初始化直线自动拉直相关设置 // 直线拉直灵敏度也在这里初始化,即使它存储在InkToShape中 diff --git a/Ink Canvas/MainWindow_cs/MW_SimulatePressure&InkToShape.cs b/Ink Canvas/MainWindow_cs/MW_SimulatePressure&InkToShape.cs index eb85e37f..f3b3283f 100644 --- a/Ink Canvas/MainWindow_cs/MW_SimulatePressure&InkToShape.cs +++ b/Ink Canvas/MainWindow_cs/MW_SimulatePressure&InkToShape.cs @@ -718,6 +718,18 @@ namespace Ink_Canvas var endP = new Point(result.Centroid.X + result.ShapeWidth / 2, result.Centroid.Y + result.ShapeHeight / 2); + // WinRT 返回的热点顺序/方向不稳定时,用点集反推 IACore 风格椭圆参数(中心/长短轴/方向/四个端点) + var hasEllipseParams = TryEstimateEllipseParamsFromStrokes( + result.StrokesToRemove, + out var ellipseCentroid, + out var ellipseA, + out var ellipseB, + out var ellipseThetaRad, + out var ellipseMajor0, + out var ellipseMajor1, + out var ellipseMinor0, + out var ellipseMinor1); + foreach (var circle in circles) //判断是否画同心椭圆 if (Math.Abs(result.Centroid.X - circle.Centroid.X) / a < 0.2 && @@ -805,13 +817,17 @@ namespace Ink_Canvas } } - //纠正垂直与水平关系 - var newPoints = FixPointsDirection(p[0], p[2]); - p[0] = newPoints[0]; - p[2] = newPoints[1]; - newPoints = FixPointsDirection(p[1], p[3]); - p[1] = newPoints[0]; - p[3] = newPoints[1]; + // 用反推参数替换中心与长短轴(比 WinRT 的包围盒更接近 IACore,且不会竖横翻转) + if (hasEllipseParams) + { + result.Centroid = ellipseCentroid; + a = ellipseA; + b = ellipseB; + iniP = new Point(result.Centroid.X - a, result.Centroid.Y - b); + endP = new Point(result.Centroid.X + a, result.Centroid.Y + b); + // 用端点重写热点,保证后续回退分支也一致 + p = new PointCollection { ellipseMajor0, ellipseMinor0, ellipseMajor1, ellipseMinor1 }; + } var pointList = GenerateEllipseGeometry(iniP, endP); var point = new StylusPointCollection(pointList); @@ -823,9 +839,8 @@ namespace Ink_Canvas if (needRotation) { var m = new Matrix(); - var fe = e.Source as FrameworkElement; - var tanTheta = (p[2].Y - p[0].Y) / (p[2].X - p[0].X); - var theta = Math.Atan(tanTheta); + // 优先使用反推参数角度;否则用端点向量角度(使用 Atan2 避免斜率无穷) + var theta = hasEllipseParams ? ellipseThetaRad : Math.Atan2(p[2].Y - p[0].Y, p[2].X - p[0].X); m.RotateAt(theta * 180.0 / Math.PI, result.Centroid.X, result.Centroid.Y); stroke.Transform(m, false); } @@ -2282,6 +2297,124 @@ namespace Ink_Canvas return new Point[2] { p1, p2 }; } + /// + /// 用点集拟合出 IACore 风格椭圆参数(中心/长短半轴/方向/四个端点)。 + /// 解决 WinRT 返回热点顺序不稳定导致椭圆纠正角度翻转的问题。 + /// + private static bool TryEstimateEllipseParamsFromStrokes( + StrokeCollection strokes, + out Point centroid, + out double a, + out double b, + out double thetaRad, + out Point major0, + out Point major1, + out Point minor0, + out Point minor1) + { + centroid = default; + a = b = 0; + thetaRad = 0; + major0 = major1 = minor0 = minor1 = default; + + if (strokes == null || strokes.Count == 0) return false; + + var pts = new List(256); + foreach (var s in strokes) + { + if (s?.StylusPoints == null) continue; + foreach (var sp in s.StylusPoints) + pts.Add(sp.ToPoint()); + } + + if (pts.Count < 12) return false; + + double mx = 0, my = 0; + for (int i = 0; i < pts.Count; i++) + { + mx += pts[i].X; + my += pts[i].Y; + } + mx /= pts.Count; + my /= pts.Count; + centroid = new Point(mx, my); + + double sxx = 0, syy = 0, sxy = 0; + for (int i = 0; i < pts.Count; i++) + { + var dx = pts[i].X - mx; + var dy = pts[i].Y - my; + sxx += dx * dx; + syy += dy * dy; + sxy += dx * dy; + } + + if (sxx + syy < 1e-6) return false; + + thetaRad = 0.5 * Math.Atan2(2.0 * sxy, sxx - syy); + if (double.IsNaN(thetaRad) || double.IsInfinity(thetaRad)) return false; + + // 主轴单位向量 v1=(cos,sin),次轴 v2=(-sin,cos) + var cos = Math.Cos(thetaRad); + var sin = Math.Sin(thetaRad); + + // 投影收集,用分位数抑制离群点 + var us = new double[pts.Count]; + var vs = new double[pts.Count]; + double maxU = double.MinValue, minU = double.MaxValue; + double maxV = double.MinValue, minV = double.MaxValue; + for (int i = 0; i < pts.Count; i++) + { + var dx = pts[i].X - mx; + var dy = pts[i].Y - my; + var u = dx * cos + dy * sin; + var v = -dx * sin + dy * cos; + us[i] = u; + vs[i] = v; + if (u > maxU) maxU = u; + if (u < minU) minU = u; + if (v > maxV) maxV = v; + if (v < minV) minV = v; + } + + Array.Sort(us); + Array.Sort(vs); + + int hi = (int)Math.Round((pts.Count - 1) * 0.98); + int lo = (int)Math.Round((pts.Count - 1) * 0.02); + hi = Math.Max(0, Math.Min(pts.Count - 1, hi)); + lo = Math.Max(0, Math.Min(pts.Count - 1, lo)); + + var uHi = us[hi]; + var uLo = us[lo]; + var vHi = vs[hi]; + var vLo = vs[lo]; + + var aCandidate = Math.Max(Math.Abs(uHi), Math.Abs(uLo)); + var bCandidate = Math.Max(Math.Abs(vHi), Math.Abs(vLo)); + if (aCandidate < 1e-3) aCandidate = Math.Max(Math.Abs(maxU), Math.Abs(minU)); + if (bCandidate < 1e-3) bCandidate = Math.Max(Math.Abs(maxV), Math.Abs(minV)); + + a = aCandidate; + b = bCandidate; + + // 保证 a 为长半轴 + if (b > a) + { + var t = a; a = b; b = t; + thetaRad += Math.PI / 2; + cos = Math.Cos(thetaRad); + sin = Math.Sin(thetaRad); + } + + major0 = new Point(mx - a * cos, my - a * sin); + major1 = new Point(mx + a * cos, my + a * sin); + minor0 = new Point(mx + b * sin, my - b * cos); + minor1 = new Point(mx - b * sin, my + b * cos); + + return a > 1e-2 && b > 1e-2; + } + public StylusPointCollection GenerateFakePressureTriangle(StylusPointCollection points) { var newPoint = new StylusPointCollection(); diff --git a/Ink Canvas/Properties/AssemblyInfo.cs b/Ink Canvas/Properties/AssemblyInfo.cs index affa789e..89688309 100644 --- a/Ink Canvas/Properties/AssemblyInfo.cs +++ b/Ink Canvas/Properties/AssemblyInfo.cs @@ -43,5 +43,5 @@ using System.Windows; // You can specify all the values or you can default the Build and Revision Numbers // by using the '*' as shown below: // [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("1.7.18.9")] -[assembly: AssemblyFileVersion("1.7.18.9")] +[assembly: AssemblyVersion("1.7.18.10")] +[assembly: AssemblyFileVersion("1.7.18.10")] diff --git a/Ink Canvas/Properties/Strings.en-US.resx b/Ink Canvas/Properties/Strings.en-US.resx index 7aecff4a..0665ba02 100644 --- a/Ink Canvas/Properties/Strings.en-US.resx +++ b/Ink Canvas/Properties/Strings.en-US.resx @@ -996,6 +996,12 @@ Canvas + + Disable hardware acceleration + + + # Improves compatibility but may reduce performance; some effects may require an app restart to fully apply. + Show pen cursor diff --git a/Ink Canvas/Properties/Strings.resx b/Ink Canvas/Properties/Strings.resx index b1d1e256..27aa9e9e 100644 --- a/Ink Canvas/Properties/Strings.resx +++ b/Ink Canvas/Properties/Strings.resx @@ -576,6 +576,12 @@ 自动:64 位进程使用 WinRT(Windows 10+),32 位使用 IACore。可强制指定 IACore 或 WinRT。 + + 手写体纠正 + + + # 开启后,将对手写笔画进行更平滑、更工整的纠正处理(基于 WinRT)。 + 自动 @@ -1038,6 +1044,12 @@ 画板 + + 关闭硬件加速 + + + # 关闭后可提升兼容性,但可能降低性能;部分效果可能需要重启程序后完全生效。 + 显示画笔光标