feat: WinRT Ink Analysis 性能优化重构方案
Co-authored-by: traeagent <traeagent@users.noreply.github.com>
This commit is contained in:
@@ -11,7 +11,6 @@ namespace Ink_Canvas.Helpers
|
||||
private readonly object _initSync = new object();
|
||||
|
||||
private ModernInkProcessor _modernProcessor;
|
||||
private ModernInkAnalyzer _modernAnalyzer;
|
||||
private bool _isModernSystemAvailable;
|
||||
private bool _isInitialized;
|
||||
|
||||
@@ -63,22 +62,20 @@ namespace Ink_Canvas.Helpers
|
||||
|
||||
private void EnsureModernAnalyzerInitialized()
|
||||
{
|
||||
if (_modernAnalyzer != null || !_isModernSystemAvailable) return;
|
||||
if (_modernProcessor != null || !_isModernSystemAvailable) return;
|
||||
|
||||
lock (_initSync)
|
||||
{
|
||||
if (_modernAnalyzer != null || !_isModernSystemAvailable) return;
|
||||
if (_modernProcessor != null || !_isModernSystemAvailable) return;
|
||||
try
|
||||
{
|
||||
_modernProcessor ??= new ModernInkProcessor();
|
||||
_modernAnalyzer = new ModernInkAnalyzer();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogHelper.WriteLogToFile("WinRT 墨迹模块懒加载失败: " + ex.Message, LogHelper.LogType.Warning);
|
||||
_isModernSystemAvailable = false;
|
||||
_modernProcessor = null;
|
||||
_modernAnalyzer = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -165,10 +162,10 @@ namespace Ink_Canvas.Helpers
|
||||
}
|
||||
|
||||
EnsureModernAnalyzerInitialized();
|
||||
if (_modernAnalyzer == null)
|
||||
if (_modernProcessor == null)
|
||||
{
|
||||
LogHelper.WriteLogToFile(
|
||||
"[手写体] CorrectInkAsync 跳过:ModernInkAnalyzer 未就绪(WinRT 初始化失败?)。笔画数=" +
|
||||
"[手写体] CorrectInkAsync 跳过:ModernInkProcessor 未就绪(WinRT 初始化失败?)。笔画数=" +
|
||||
strokes.Count,
|
||||
LogHelper.LogType.Warning);
|
||||
return Task.FromResult(strokes);
|
||||
@@ -178,7 +175,7 @@ namespace Ink_Canvas.Helpers
|
||||
"[手写体] CorrectInkAsync 开始:笔画数=" + strokes.Count +
|
||||
",字体=" + (string.IsNullOrWhiteSpace(handwritingFontFamilyList) ? "(默认)" : handwritingFontFamilyList.Trim()),
|
||||
LogHelper.LogType.Info);
|
||||
return _modernAnalyzer.AnalyzeAndCorrectAsync(strokes, handwritingFontFamilyList);
|
||||
return WinRtHandwritingRecognizer.ConvertRecognizedTextToHandwritingInkAsync(strokes, handwritingFontFamilyList);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
@@ -234,7 +231,6 @@ namespace Ink_Canvas.Helpers
|
||||
public void Dispose()
|
||||
{
|
||||
_modernProcessor?.Dispose();
|
||||
_modernAnalyzer?.Dispose();
|
||||
_isInitialized = false;
|
||||
}
|
||||
}
|
||||
@@ -256,20 +252,4 @@ namespace Ink_Canvas.Helpers
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
internal sealed class ModernInkAnalyzer : IDisposable
|
||||
{
|
||||
public Task<StrokeCollection> AnalyzeAndCorrectAsync(
|
||||
StrokeCollection strokes,
|
||||
string handwritingFontFamilyList)
|
||||
{
|
||||
return WinRtHandwritingRecognizer.ConvertRecognizedTextToHandwritingInkAsync(
|
||||
strokes,
|
||||
handwritingFontFamilyList);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using OSVersionExtension;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows;
|
||||
using System.Windows.Ink;
|
||||
@@ -11,6 +12,149 @@ using WinRtInkAnalyzer = global::Windows.UI.Input.Inking.Analysis.InkAnalyzer;
|
||||
|
||||
namespace Ink_Canvas.Helpers
|
||||
{
|
||||
internal class ModernInkAnalyzer : IDisposable
|
||||
{
|
||||
public static readonly Guid ShapeStrokePropertyGuid = new Guid("11111111-2222-3333-4444-555555555555");
|
||||
|
||||
private global::Windows.UI.Input.Inking.Analysis.InkAnalyzer _internalAnalyzer;
|
||||
private readonly StrokeCollection _strokeContainer;
|
||||
private readonly Dictionary<Stroke, uint> _strokeIdMap = new Dictionary<Stroke, uint>();
|
||||
private readonly Dictionary<uint, Stroke> _reverseIdMap = new Dictionary<uint, Stroke>();
|
||||
private readonly object _syncLock = new object();
|
||||
|
||||
public ModernInkAnalyzer(StrokeCollection container)
|
||||
{
|
||||
if (!WinRtInkShapeRecognizer.IsApiAvailable)
|
||||
return;
|
||||
|
||||
_internalAnalyzer = new global::Windows.UI.Input.Inking.Analysis.InkAnalyzer();
|
||||
_strokeContainer = container;
|
||||
_strokeContainer.StrokesChanged += OnStrokesChanged;
|
||||
|
||||
// Initial sync
|
||||
foreach (var stroke in _strokeContainer)
|
||||
{
|
||||
AddStrokeInternal(stroke);
|
||||
}
|
||||
}
|
||||
|
||||
private void OnStrokesChanged(object sender, StrokeCollectionChangedEventArgs e)
|
||||
{
|
||||
if (_internalAnalyzer == null) return;
|
||||
|
||||
lock (_syncLock)
|
||||
{
|
||||
foreach (var stroke in e.Added)
|
||||
{
|
||||
AddStrokeInternal(stroke);
|
||||
}
|
||||
|
||||
foreach (var stroke in e.Removed)
|
||||
{
|
||||
if (_strokeIdMap.TryGetValue(stroke, out var id))
|
||||
{
|
||||
_internalAnalyzer.RemoveDataForStroke(id);
|
||||
_strokeIdMap.Remove(stroke);
|
||||
_reverseIdMap.Remove(id);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void AddStrokeInternal(Stroke stroke)
|
||||
{
|
||||
if (stroke.ContainsPropertyData(ShapeStrokePropertyGuid))
|
||||
return;
|
||||
|
||||
var inkStroke = WinRtInkShapeRecognizer.CreateInkStrokeFromWpf(stroke);
|
||||
if (inkStroke == null) return;
|
||||
|
||||
_internalAnalyzer.AddDataForStroke(inkStroke);
|
||||
_internalAnalyzer.SetStrokeDataKind(
|
||||
inkStroke.Id,
|
||||
global::Windows.UI.Input.Inking.Analysis.InkAnalysisStrokeKind.Drawing);
|
||||
|
||||
_strokeIdMap[stroke] = inkStroke.Id;
|
||||
_reverseIdMap[inkStroke.Id] = stroke;
|
||||
}
|
||||
|
||||
private CancellationTokenSource _cts;
|
||||
|
||||
public async Task<InkShapeRecognitionResult> AnalyzeAsync()
|
||||
{
|
||||
if (_internalAnalyzer == null)
|
||||
return InkShapeRecognitionResult.Empty;
|
||||
|
||||
_cts?.Cancel();
|
||||
_cts = new CancellationTokenSource();
|
||||
var token = _cts.Token;
|
||||
|
||||
try
|
||||
{
|
||||
var result = await _internalAnalyzer.AnalyzeAsync().AsTask(token).ConfigureAwait(true);
|
||||
|
||||
if (token.IsCancellationRequested) return InkShapeRecognitionResult.Empty;
|
||||
|
||||
// Use the internal method from WinRtInkShapeRecognizer to find the primary drawing
|
||||
var drawing = WinRtInkShapeRecognizer.FindPrimaryDrawing(_internalAnalyzer);
|
||||
if (drawing == null)
|
||||
return InkShapeRecognitionResult.Empty;
|
||||
|
||||
if (drawing.DrawingKind == global::Windows.UI.Input.Inking.Analysis.InkAnalysisDrawingKind.Drawing)
|
||||
return InkShapeRecognitionResult.Empty;
|
||||
|
||||
var name = WinRtInkShapeRecognizer.MapDrawingKindToShapeName(drawing.DrawingKind);
|
||||
if (string.IsNullOrEmpty(name) || name == "Drawing")
|
||||
return InkShapeRecognitionResult.Empty;
|
||||
|
||||
var winPts = WinRtInkShapeRecognizer.CopyWinRtPoints(drawing);
|
||||
var hot = WinRtInkShapeRecognizer.ToWpfPointCollection(winPts);
|
||||
var c = drawing.Center;
|
||||
var centroid = new SysPoint(c.X, c.Y);
|
||||
WinRtInkShapeRecognizer.BoundsFromPoints(winPts, out double w, out double h);
|
||||
|
||||
var toRemove = new StrokeCollection();
|
||||
lock (_syncLock)
|
||||
{
|
||||
foreach (var id in drawing.GetStrokeIds())
|
||||
{
|
||||
if (_reverseIdMap.TryGetValue(id, out var stroke))
|
||||
{
|
||||
toRemove.Add(stroke);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (toRemove.Count == 0)
|
||||
return InkShapeRecognitionResult.Empty;
|
||||
|
||||
return new InkShapeRecognitionResult(name, centroid, hot, w, h, toRemove);
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
return InkShapeRecognitionResult.Empty;
|
||||
}
|
||||
}
|
||||
|
||||
public Task<StrokeCollection> AnalyzeAndCorrectAsync(
|
||||
StrokeCollection strokes,
|
||||
string handwritingFontFamilyList)
|
||||
{
|
||||
return WinRtHandwritingRecognizer.ConvertRecognizedTextToHandwritingInkAsync(
|
||||
strokes,
|
||||
handwritingFontFamilyList);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (_strokeContainer != null)
|
||||
{
|
||||
_strokeContainer.StrokesChanged -= OnStrokesChanged;
|
||||
}
|
||||
_internalAnalyzer = null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>基于 Windows.UI.Input.Inking.Analysis 的形状识别(适用于 64 位进程等场景)。</summary>
|
||||
internal static class WinRtInkShapeRecognizer
|
||||
{
|
||||
@@ -150,8 +294,8 @@ namespace Ink_Canvas.Helpers
|
||||
return builder.CreateStroke(points);
|
||||
}
|
||||
|
||||
private static global::Windows.UI.Input.Inking.Analysis.InkAnalysisInkDrawing FindPrimaryDrawing(
|
||||
WinRtInkAnalyzer analyzer)
|
||||
internal static global::Windows.UI.Input.Inking.Analysis.InkAnalysisInkDrawing FindPrimaryDrawing(
|
||||
global::Windows.UI.Input.Inking.Analysis.InkAnalyzer analyzer)
|
||||
{
|
||||
global::Windows.UI.Input.Inking.Analysis.InkAnalysisInkDrawing best = null;
|
||||
double bestArea = -1;
|
||||
@@ -190,7 +334,7 @@ namespace Ink_Canvas.Helpers
|
||||
return w * h;
|
||||
}
|
||||
|
||||
private static global::Windows.Foundation.Point[] CopyWinRtPoints(
|
||||
internal static global::Windows.Foundation.Point[] CopyWinRtPoints(
|
||||
global::Windows.UI.Input.Inking.Analysis.InkAnalysisInkDrawing drawing)
|
||||
{
|
||||
var src = drawing?.Points;
|
||||
@@ -207,7 +351,7 @@ namespace Ink_Canvas.Helpers
|
||||
return arr;
|
||||
}
|
||||
|
||||
private static void BoundsFromPoints(
|
||||
internal static void BoundsFromPoints(
|
||||
System.Collections.Generic.IReadOnlyList<global::Windows.Foundation.Point> points,
|
||||
out double w,
|
||||
out double h)
|
||||
@@ -232,7 +376,7 @@ namespace Ink_Canvas.Helpers
|
||||
h = Math.Max(0, maxY - minY);
|
||||
}
|
||||
|
||||
private static PointCollection ToWpfPointCollection(
|
||||
internal static PointCollection ToWpfPointCollection(
|
||||
System.Collections.Generic.IReadOnlyList<global::Windows.Foundation.Point> points)
|
||||
{
|
||||
var hot = new PointCollection();
|
||||
@@ -246,7 +390,7 @@ namespace Ink_Canvas.Helpers
|
||||
return hot;
|
||||
}
|
||||
|
||||
private static string MapDrawingKindToShapeName(
|
||||
internal static string MapDrawingKindToShapeName(
|
||||
global::Windows.UI.Input.Inking.Analysis.InkAnalysisDrawingKind kind)
|
||||
{
|
||||
switch (kind)
|
||||
|
||||
@@ -30,6 +30,10 @@ namespace Ink_Canvas
|
||||
/// </remarks>
|
||||
public partial class MainWindow : Window
|
||||
{
|
||||
private Helpers.ModernInkAnalyzer _modernInkAnalyzer;
|
||||
private Helpers.ModernInkAnalyzer ModernInkAnalyzer =>
|
||||
_modernInkAnalyzer ??= new Helpers.ModernInkAnalyzer(inkCanvas.Strokes);
|
||||
|
||||
/// <summary>
|
||||
/// 存储新的笔画集合,用于形状识别
|
||||
/// </summary>
|
||||
@@ -564,6 +568,7 @@ namespace Ink_Canvas
|
||||
{
|
||||
DrawingAttributes = inkCanvas.DefaultDrawingAttributes.Clone()
|
||||
};
|
||||
straightStroke.AddPropertyData(Helpers.ModernInkAnalyzer.ShapeStrokePropertyGuid, true);
|
||||
|
||||
// Replace the original stroke with the straightened one
|
||||
SetNewBackupOfStroke();
|
||||
@@ -617,17 +622,26 @@ namespace Ink_Canvas
|
||||
ProcessRectangleGuideLines(e.Stroke);
|
||||
|
||||
var shapeMode = ShapeRecognitionRouter.FromSettingsInt(Settings.InkToShape.ShapeRecognitionEngine);
|
||||
var strokeReco = new StrokeCollection();
|
||||
var result = await InkRecognizeHelper.RecognizeShapeUnifiedAsync(newStrokes, shapeMode);
|
||||
for (var i = newStrokes.Count - 1; i >= 0; i--)
|
||||
InkShapeRecognitionResult result = InkShapeRecognitionResult.Empty;
|
||||
|
||||
if (ShapeRecognitionRouter.ResolveUseWinRt(shapeMode) && Helpers.WinRtInkShapeRecognizer.IsApiAvailable)
|
||||
{
|
||||
strokeReco.Add(newStrokes[i]);
|
||||
var newResult = await InkRecognizeHelper.RecognizeShapeUnifiedAsync(strokeReco, shapeMode);
|
||||
if (newResult.IsSuccess &&
|
||||
(newResult.ShapeName == "Circle" || newResult.ShapeName == "Ellipse"))
|
||||
result = await ModernInkAnalyzer.AnalyzeAsync();
|
||||
}
|
||||
else
|
||||
{
|
||||
var strokeReco = new StrokeCollection();
|
||||
result = await InkRecognizeHelper.RecognizeShapeUnifiedAsync(newStrokes, shapeMode);
|
||||
for (var i = newStrokes.Count - 1; i >= 0; i--)
|
||||
{
|
||||
result = newResult;
|
||||
break;
|
||||
strokeReco.Add(newStrokes[i]);
|
||||
var newResult = await InkRecognizeHelper.RecognizeShapeUnifiedAsync(strokeReco, shapeMode);
|
||||
if (newResult.IsSuccess &&
|
||||
(newResult.ShapeName == "Circle" || newResult.ShapeName == "Ellipse"))
|
||||
{
|
||||
result = newResult;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -687,6 +701,7 @@ namespace Ink_Canvas
|
||||
{
|
||||
DrawingAttributes = inkCanvas.DefaultDrawingAttributes.Clone()
|
||||
};
|
||||
stroke.AddPropertyData(Helpers.ModernInkAnalyzer.ShapeStrokePropertyGuid, true);
|
||||
circles.Add(new Circle(result.Centroid, result.ShapeWidth / 2.0, stroke));
|
||||
SetNewBackupOfStroke();
|
||||
_currentCommitType = CommitReason.ShapeRecognition;
|
||||
@@ -790,6 +805,7 @@ namespace Ink_Canvas
|
||||
{
|
||||
DrawingAttributes = inkCanvas.DefaultDrawingAttributes.Clone()
|
||||
};
|
||||
_stroke.AddPropertyData(Helpers.ModernInkAnalyzer.ShapeStrokePropertyGuid, true);
|
||||
var _dashedLineStroke =
|
||||
GenerateDashedLineEllipseStrokeCollection(iniP, endP, true, false);
|
||||
var strokes = new StrokeCollection {
|
||||
@@ -836,6 +852,7 @@ namespace Ink_Canvas
|
||||
{
|
||||
DrawingAttributes = inkCanvas.DefaultDrawingAttributes.Clone()
|
||||
};
|
||||
stroke.AddPropertyData(Helpers.ModernInkAnalyzer.ShapeStrokePropertyGuid, true);
|
||||
|
||||
if (needRotation)
|
||||
{
|
||||
@@ -883,6 +900,7 @@ namespace Ink_Canvas
|
||||
{
|
||||
DrawingAttributes = inkCanvas.DefaultDrawingAttributes.Clone()
|
||||
};
|
||||
stroke.AddPropertyData(Helpers.ModernInkAnalyzer.ShapeStrokePropertyGuid, true);
|
||||
SetNewBackupOfStroke();
|
||||
_currentCommitType = CommitReason.ShapeRecognition;
|
||||
inkCanvas.Strokes.Remove(result.StrokesToRemove);
|
||||
@@ -928,6 +946,7 @@ namespace Ink_Canvas
|
||||
{
|
||||
DrawingAttributes = inkCanvas.DefaultDrawingAttributes.Clone()
|
||||
};
|
||||
stroke.AddPropertyData(Helpers.ModernInkAnalyzer.ShapeStrokePropertyGuid, true);
|
||||
SetNewBackupOfStroke();
|
||||
_currentCommitType = CommitReason.ShapeRecognition;
|
||||
inkCanvas.Strokes.Remove(result.StrokesToRemove);
|
||||
@@ -2835,6 +2854,7 @@ namespace Ink_Canvas
|
||||
{
|
||||
DrawingAttributes = inkCanvas.DefaultDrawingAttributes.Clone()
|
||||
};
|
||||
rectangleStroke.AddPropertyData(Helpers.ModernInkAnalyzer.ShapeStrokePropertyGuid, true);
|
||||
|
||||
// 移除原有的四条直线
|
||||
SetNewBackupOfStroke();
|
||||
|
||||
Reference in New Issue
Block a user