代码优化

This commit is contained in:
2026-04-05 18:52:19 +08:00
parent bad05f77b5
commit ebbe018bae
11 changed files with 147 additions and 92 deletions
+3 -2
View File
@@ -2,6 +2,7 @@
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:Ink_Canvas" xmlns:local="clr-namespace:Ink_Canvas"
xmlns:props="clr-namespace:Ink_Canvas.Properties"
xmlns:tb="clr-namespace:H.NotifyIcon;assembly=H.NotifyIcon.Wpf" xmlns:tb="clr-namespace:H.NotifyIcon;assembly=H.NotifyIcon.Wpf"
xmlns:ui="http://schemas.inkore.net/lib/ui/wpf/modern" xmlns:ui="http://schemas.inkore.net/lib/ui/wpf/modern"
xmlns:ikw="http://schemas.inkore.net/lib/ui/wpf" xmlns:ikw="http://schemas.inkore.net/lib/ui/wpf"
@@ -35,7 +36,7 @@
<MenuItem Name="TempShowMainWindowTrayIconMenuItem" Click="TempShowMainWindowTrayIconMenuItem_Clicked"> <MenuItem Name="TempShowMainWindowTrayIconMenuItem" Click="TempShowMainWindowTrayIconMenuItem_Clicked">
<MenuItem.Header> <MenuItem.Header>
<ikw:SimpleStackPanel Orientation="Horizontal" Margin="-4,0,0,0"> <ikw:SimpleStackPanel Orientation="Horizontal" Margin="-4,0,0,0">
<TextBlock FontSize="14" VerticalAlignment="Center" Foreground="#18181b" Text="显示主窗口(2分钟)" /> <TextBlock FontSize="14" VerticalAlignment="Center" Foreground="#18181b" Text="{x:Static props:Strings.Tray_TempShowMainWindow}" />
</ikw:SimpleStackPanel> </ikw:SimpleStackPanel>
</MenuItem.Header> </MenuItem.Header>
<MenuItem.Icon> <MenuItem.Icon>
@@ -55,7 +56,7 @@
<MenuItem Name="OpenSettingsTrayIconMenuItem" Click="OpenSettingsTrayIconMenuItem_Clicked"> <MenuItem Name="OpenSettingsTrayIconMenuItem" Click="OpenSettingsTrayIconMenuItem_Clicked">
<MenuItem.Header> <MenuItem.Header>
<ikw:SimpleStackPanel Orientation="Horizontal" Margin="-4,0,0,0"> <ikw:SimpleStackPanel Orientation="Horizontal" Margin="-4,0,0,0">
<TextBlock FontSize="14" VerticalAlignment="Center" Foreground="#18181b" Text="打开设置" /> <TextBlock FontSize="14" VerticalAlignment="Center" Foreground="#18181b" Text="{x:Static props:Strings.Tray_OpenSettings}" />
</ikw:SimpleStackPanel> </ikw:SimpleStackPanel>
</MenuItem.Header> </MenuItem.Header>
<MenuItem.Icon> <MenuItem.Icon>
+6 -2
View File
@@ -48,13 +48,17 @@ namespace Ink_Canvas.Helpers
break; break;
} }
if (alternates.Count > 0) if (alternates.Count > 0)
analysisAlternate = alternates[0]; {
var altFinal = alternates[0];
if (altFinal?.AlternateNodes != null && altFinal.AlternateNodes.Count > 0)
analysisAlternate = altFinal;
}
} }
} }
analyzer.Dispose(); 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; var node = analysisAlternate.AlternateNodes[0] as InkDrawingNode;
if (node == null) if (node == null)
+6 -9
View File
@@ -49,16 +49,13 @@ namespace Ink_Canvas.Helpers
if (string.IsNullOrWhiteSpace(softwareName)) if (string.IsNullOrWhiteSpace(softwareName))
return null; return null;
// 64 位进程默认只枚举 64 位注册表视图;32 位希沃常写在 WOW6432Node 下,需一并扫描。 // 须用 OpenBaseKey + RegistryView 显式指定视图:Registry.LocalMachine.OpenSubKey 跟随进程位数,
string[] uninstallRoots = // 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", using (RegistryKey baseKey = RegistryKey.OpenBaseKey(RegistryHive.LocalMachine, view))
@"SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall", using (RegistryKey key = baseKey.OpenSubKey(uninstallSubKey))
};
foreach (string root in uninstallRoots)
{
using (RegistryKey key = Registry.LocalMachine.OpenSubKey(root))
{ {
if (key == null) continue; if (key == null) continue;
string found = FindInUninstallKey(key, softwareName); string found = FindInUninstallKey(key, softwareName);
+4 -4
View File
@@ -100,8 +100,8 @@ namespace Ink_Canvas.Helpers
var item = _currentStrokeHistory[_currentIndex]; var item = _currentStrokeHistory[_currentIndex];
item.StrokeHasBeenCleared = !item.StrokeHasBeenCleared; item.StrokeHasBeenCleared = !item.StrokeHasBeenCleared;
_currentIndex--; _currentIndex--;
OnUndoStateChanged?.Invoke(_currentIndex > -1); OnUndoStateChanged?.Invoke(CanUndo);
OnRedoStateChanged?.Invoke(_currentStrokeHistory.Count - _currentIndex - 1 > 0); OnRedoStateChanged?.Invoke(CanRedo);
return item; return item;
} }
@@ -137,8 +137,8 @@ namespace Ink_Canvas.Helpers
} }
private void NotifyUndoRedoState() private void NotifyUndoRedoState()
{ {
OnUndoStateChanged?.Invoke(_currentIndex > -1); OnUndoStateChanged?.Invoke(CanUndo);
OnRedoStateChanged?.Invoke(_currentStrokeHistory.Count - _currentIndex - 1 > 0); OnRedoStateChanged?.Invoke(CanRedo);
} }
/// <summary>当前历史是否允许撤销。</summary> /// <summary>当前历史是否允许撤销。</summary>
@@ -40,7 +40,9 @@ namespace Ink_Canvas.Helpers
{ {
try try
{ {
await RecognizeHandwritingAsync(new StrokeCollection()).ConfigureAwait(true); await RecognizeHandwritingAsync(
WinRtInkShapeRecognizer.CreateMinimalWarmupStrokeCollection(),
verboseTrace: false).ConfigureAwait(true);
} }
catch catch
{ {
@@ -59,12 +61,15 @@ namespace Ink_Canvas.Helpers
/// 再对每一分词用 <see cref="WinRtInk.InkRecognizerContainer"/> 取 <c>GetTextCandidates</c>(与当前 SDK 中部分版本的 /// 再对每一分词用 <see cref="WinRtInk.InkRecognizerContainer"/> 取 <c>GetTextCandidates</c>(与当前 SDK 中部分版本的
/// <see cref="WinRtInk.InkRecognitionResult"/> 未暴露笔画映射的局限兼容)。 /// <see cref="WinRtInk.InkRecognitionResult"/> 未暴露笔画映射的局限兼容)。
/// </summary> /// </summary>
public static async Task<HandwritingRecognitionResult> RecognizeHandwritingAsync(StrokeCollection strokes) /// <param name="verboseTrace">为 false 时跳过详细识别日志(用于 <see cref="Warmup"/> 等)。</param>
public static async Task<HandwritingRecognitionResult> RecognizeHandwritingAsync(
StrokeCollection strokes,
bool verboseTrace = true)
{ {
if (!IsApiAvailable || strokes == null || strokes.Count == 0) if (!IsApiAvailable || strokes == null || strokes.Count == 0)
return HandwritingRecognitionResult.Empty; return HandwritingRecognitionResult.Empty;
var traceRecognition = strokes.Count > 0; var traceRecognition = verboseTrace;
try try
{ {
+19 -1
View File
@@ -28,7 +28,8 @@ namespace Ink_Canvas.Helpers
{ {
try try
{ {
await RecognizeShapeAsync(new StrokeCollection()); // 空 StrokeCollection 在 RecognizeShapeAsync 入口会直接返回,无法预热 WinRT InkAnalyzer。
await RecognizeShapeAsync(CreateMinimalWarmupStrokeCollection()).ConfigureAwait(true);
} }
catch catch
{ {
@@ -99,6 +100,23 @@ namespace Ink_Canvas.Helpers
} }
} }
/// <summary>
/// 极短合成笔画,供 <see cref="Warmup"/> 等场景走完整 WinRT 转换与分析管线(空集合在入口处会被直接返回)。
/// </summary>
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;
}
/// <summary>供 WinRT 手写等模块复用:将 WPF <see cref="Stroke"/> 转为 WinRT <see cref="global::Windows.UI.Input.Inking.InkStroke"/>。</summary> /// <summary>供 WinRT 手写等模块复用:将 WPF <see cref="Stroke"/> 转为 WinRT <see cref="global::Windows.UI.Input.Inking.InkStroke"/>。</summary>
internal static global::Windows.UI.Input.Inking.InkStroke CreateInkStrokeFromWpf(Stroke stroke) internal static global::Windows.UI.Input.Inking.InkStroke CreateInkStrokeFromWpf(Stroke stroke)
{ {
@@ -176,85 +176,89 @@ namespace Ink_Canvas
} }
catch (Exception ex) { System.Diagnostics.Debug.WriteLine(ex); } catch (Exception ex) { System.Diagnostics.Debug.WriteLine(ex); }
switch (Settings.Canvas.InkStyle) // 「屏蔽压感」已在收笔主路径将点集归一成 0.5;此处若再跑 InkStyle 0/1 会重写 PressureFactor,造成假压感。
if (!Settings.Canvas.DisablePressure)
{ {
case 1: switch (Settings.Canvas.InkStyle)
if (penType == 0) {
try case 1:
{ if (penType == 0)
var stylusPoints = new StylusPointCollection(); try
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(), var stylusPoints = new StylusPointCollection();
e.Stroke.StylusPoints[i].ToPoint(), var n = e.Stroke.StylusPoints.Count - 1;
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); }
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++) for (var i = 0; i <= n; i++)
{ {
var point = new StylusPoint(); var speed = GetPointSpeed(e.Stroke.StylusPoints[Math.Max(i - 1, 0)].ToPoint(),
e.Stroke.StylusPoints[i].ToPoint(),
point.PressureFactor = (float)(0.4 * (n - i) / n + pressure); e.Stroke.StylusPoints[Math.Min(i + 1, n)].ToPoint());
point.X = e.Stroke.StylusPoints[i].X; var point = new StylusPoint
point.Y = e.Stroke.StylusPoints[i].Y; {
PressureFactor = RateBasedPressureFactorFromPointSpeed(speed),
X = e.Stroke.StylusPoints[i].X,
Y = e.Stroke.StylusPoints[i].Y
};
stylusPoints.Add(point); stylusPoints.Add(point);
} }
e.Stroke.StylusPoints = stylusPoints;
} }
catch (Exception ex) { System.Diagnostics.Debug.WriteLine(ex); }
e.Stroke.StylusPoints = stylusPoints; break;
} case 0:
catch (Exception ex) { System.Diagnostics.Debug.WriteLine(ex); } 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; point.PressureFactor = (float)0.5;
case 3: point.X = e.Stroke.StylusPoints[i].X;
break; 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); } catch (Exception ex) { System.Diagnostics.Debug.WriteLine(ex); }
@@ -507,7 +511,11 @@ namespace Ink_Canvas
// 实时笔锋:勿依赖 DrawingAttributes.IgnorePressure。无压感触摸/鼠标等设备上,运行时仍可能为 true, // 实时笔锋:勿依赖 DrawingAttributes.IgnorePressure。无压感触摸/鼠标等设备上,运行时仍可能为 true,
// 会导致不进入逻辑或进入后渲染仍忽略 PressureFactor;具体在 ApplyVelocityBrushTipFromSpeed 内关闭。 // 会导致不进入逻辑或进入后渲染仍忽略 PressureFactor;具体在 ApplyVelocityBrushTipFromSpeed 内关闭。
// 「屏蔽压感」时必须跳过:否则会重写 PressureFactor 并强制 IgnorePressure=false,与归一压感冲突。
// VelocityBrushTipMix <= 0 时 ApplyVelocityBrushTipFromSpeed 为空操作,无需调用。
if (Settings.Canvas.InkStyle == 3 if (Settings.Canvas.InkStyle == 3
&& Settings.Canvas.VelocityBrushTipMix > 0
&& !Settings.Canvas.DisablePressure
&& !touchPressureSimulationApplied && !touchPressureSimulationApplied
&& penType != 1 && penType != 1
&& e.Stroke?.DrawingAttributes != null && e.Stroke?.DrawingAttributes != null
@@ -2183,12 +2191,16 @@ namespace Ink_Canvas
/// <summary> /// <summary>
/// 将沿线速度映射为压感并与硬件压感混合,快写略细、慢写略粗;在落笔时(及手写笔移动时由调用方)统一施加。 /// 将沿线速度映射为压感并与硬件压感混合,快写略细、慢写略粗;在落笔时(及手写笔移动时由调用方)统一施加。
/// 无压感设备上系统可能将 <see cref="DrawingAttributes.IgnorePressure"/> 置为 true,此处强制关闭以便粗细随合成压感变化(与「屏蔽压感」无关:调用方已保证未屏蔽) /// 无压感设备上系统可能将 <see cref="DrawingAttributes.IgnorePressure"/> 置为 true,此处强制关闭以便粗细随合成压感变化。
/// 若 <see cref="Settings.Canvas.DisablePressure"/> 为 true,本方法直接返回且不修改 IgnorePressure。
/// </summary> /// </summary>
private void ApplyVelocityBrushTipFromSpeed(Stroke stroke) private void ApplyVelocityBrushTipFromSpeed(Stroke stroke)
{ {
try try
{ {
if (Settings.Canvas.DisablePressure)
return;
var mix = Settings.Canvas.VelocityBrushTipMix; var mix = Settings.Canvas.VelocityBrushTipMix;
if (mix <= 0 || stroke == null) return; if (mix <= 0 || stroke == null) return;
if (mix > 1) mix = 1; if (mix > 1) mix = 1;
@@ -2885,7 +2897,11 @@ namespace Ink_Canvas
PruneHandwritingBeautifyBatch(); PruneHandwritingBeautifyBatch();
while (_handwritingRecentStrokesForBeautify.Count > HandwritingBeautifyMaxRecentStrokes) while (_handwritingRecentStrokesForBeautify.Count > HandwritingBeautifyMaxRecentStrokes)
{
var evicted = _handwritingRecentStrokesForBeautify[0];
_handwritingRecentStrokesForBeautify.RemoveAt(0); _handwritingRecentStrokesForBeautify.RemoveAt(0);
_handwritingBeautifyInkInputByCanvasStroke.Remove(evicted);
}
EnsureHandwritingBeautifyDebounceTimer(); EnsureHandwritingBeautifyDebounceTimer();
_handwritingBeautifyDebounceTimer.Stop(); _handwritingBeautifyDebounceTimer.Stop();
+3 -1
View File
@@ -543,10 +543,12 @@ namespace Ink_Canvas
foreach (var stylusPoint in stylusPointCollection) foreach (var stylusPoint in stylusPointCollection)
strokeVisual.Add(new StylusPoint(stylusPoint.X, stylusPoint.Y, stylusPoint.PressureFactor)); strokeVisual.Add(new StylusPoint(stylusPoint.X, stylusPoint.Y, stylusPoint.PressureFactor));
// 实时笔锋:在绘制过程中更新压感并整笔重绘预览;否则预览层固定线宽,收笔后改点集也看不到笔锋变化 // 实时笔锋:混合度 > 0 时在绘制过程中更新压感并整笔重绘预览;混合为 0 时与普通过程一致用增量 Redraw,避免每点 ForceRedraw 整笔清空(长笔画卡顿)
var committedStroke = strokeVisual.Stroke; var committedStroke = strokeVisual.Stroke;
if (committedStroke != null if (committedStroke != null
&& Settings.Canvas.InkStyle == 3 && Settings.Canvas.InkStyle == 3
&& Settings.Canvas.VelocityBrushTipMix > 0
&& !Settings.Canvas.DisablePressure
&& penType == 0 && penType == 0
&& committedStroke.DrawingAttributes != null && committedStroke.DrawingAttributes != null
&& !committedStroke.DrawingAttributes.IsHighlighter && !committedStroke.DrawingAttributes.IsHighlighter
+2
View File
@@ -119,5 +119,7 @@ namespace Ink_Canvas.Properties
public static string Nav_About => GetString(nameof(Nav_About)) ?? "关于"; public static string Nav_About => GetString(nameof(Nav_About)) ?? "关于";
public static string App_Title => GetString(nameof(App_Title)) ?? "InkCanvasforClass"; public static string App_Title => GetString(nameof(App_Title)) ?? "InkCanvasforClass";
public static string Booth_Resolution_Tooltip => GetString(nameof(Booth_Resolution_Tooltip)) ?? "展台/截图分辨率"; 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)) ?? "打开设置";
} }
} }
+2
View File
@@ -763,4 +763,6 @@
<data name="FloatingBar_Gesture_TwoFingerRotate" xml:space="preserve"><value>Two-finger rotate</value></data> <data name="FloatingBar_Gesture_TwoFingerRotate" xml:space="preserve"><value>Two-finger rotate</value></data>
<data name="Board_Gesture" xml:space="preserve"><value>Gesture</value></data> <data name="Board_Gesture" xml:space="preserve"><value>Gesture</value></data>
<data name="Board_GestureOptions" xml:space="preserve"><value>Gesture options</value></data> <data name="Board_GestureOptions" xml:space="preserve"><value>Gesture options</value></data>
<data name="Tray_TempShowMainWindow" xml:space="preserve"><value>Show main window (2 minutes)</value></data>
<data name="Tray_OpenSettings" xml:space="preserve"><value>Open settings</value></data>
</root> </root>
+8
View File
@@ -782,4 +782,12 @@
<data name="FloatingBar_Gesture_TwoFingerRotate" xml:space="preserve"><value>双指旋转</value></data> <data name="FloatingBar_Gesture_TwoFingerRotate" xml:space="preserve"><value>双指旋转</value></data>
<data name="Board_Gesture" xml:space="preserve"><value>手势</value></data> <data name="Board_Gesture" xml:space="preserve"><value>手势</value></data>
<data name="Board_GestureOptions" xml:space="preserve"><value>手势选项</value></data> <data name="Board_GestureOptions" xml:space="preserve"><value>手势选项</value></data>
<data name="Tray_TempShowMainWindow" xml:space="preserve">
<value>显示主窗口(2分钟)</value>
<comment>系统托盘 - 临时显示主窗口</comment>
</data>
<data name="Tray_OpenSettings" xml:space="preserve">
<value>打开设置</value>
<comment>系统托盘 - 打开设置</comment>
</data>
</root> </root>