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 @@
画板
+
+ 关闭硬件加速
+
+
+ # 关闭后可提升兼容性,但可能降低性能;部分效果可能需要重启程序后完全生效。
+
显示画笔光标