diff --git a/Ink Canvas.sln b/Ink Canvas.sln
index 0bf57c17..7907e2c2 100644
--- a/Ink Canvas.sln
+++ b/Ink Canvas.sln
@@ -9,6 +9,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "InkCanvas.PluginSdk", "InkC
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "InkCanvas.Controls", "InkCanvas.Controls\InkCanvas.Controls.csproj", "{A1B2C3D4-E5F6-7890-ABCD-EF1234567890}"
EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "InkCanvasForClass.IACoreHelper", "InkCanvasForClass.IACoreHelper\InkCanvasForClass.IACoreHelper.csproj", "{B1A2C3D4-E5F6-7890-ABCD-EF1234567891}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -83,6 +85,26 @@ Global
{A1B2C3D4-E5F6-7890-ABCD-EF1234567890}.Release|x64.Build.0 = Release|Any CPU
{A1B2C3D4-E5F6-7890-ABCD-EF1234567890}.Release|x86.ActiveCfg = Release|Any CPU
{A1B2C3D4-E5F6-7890-ABCD-EF1234567890}.Release|x86.Build.0 = Release|Any CPU
+ {B1A2C3D4-E5F6-7890-ABCD-EF1234567891}.Debug|Any CPU.ActiveCfg = Debug|x86
+ {B1A2C3D4-E5F6-7890-ABCD-EF1234567891}.Debug|Any CPU.Build.0 = Debug|x86
+ {B1A2C3D4-E5F6-7890-ABCD-EF1234567891}.Debug|ARM.ActiveCfg = Debug|x86
+ {B1A2C3D4-E5F6-7890-ABCD-EF1234567891}.Debug|ARM.Build.0 = Debug|x86
+ {B1A2C3D4-E5F6-7890-ABCD-EF1234567891}.Debug|ARM64.ActiveCfg = Debug|x86
+ {B1A2C3D4-E5F6-7890-ABCD-EF1234567891}.Debug|ARM64.Build.0 = Debug|x86
+ {B1A2C3D4-E5F6-7890-ABCD-EF1234567891}.Debug|x64.ActiveCfg = Debug|x86
+ {B1A2C3D4-E5F6-7890-ABCD-EF1234567891}.Debug|x64.Build.0 = Debug|x86
+ {B1A2C3D4-E5F6-7890-ABCD-EF1234567891}.Debug|x86.ActiveCfg = Debug|x86
+ {B1A2C3D4-E5F6-7890-ABCD-EF1234567891}.Debug|x86.Build.0 = Debug|x86
+ {B1A2C3D4-E5F6-7890-ABCD-EF1234567891}.Release|Any CPU.ActiveCfg = Release|x86
+ {B1A2C3D4-E5F6-7890-ABCD-EF1234567891}.Release|Any CPU.Build.0 = Release|x86
+ {B1A2C3D4-E5F6-7890-ABCD-EF1234567891}.Release|ARM.ActiveCfg = Release|x86
+ {B1A2C3D4-E5F6-7890-ABCD-EF1234567891}.Release|ARM.Build.0 = Release|x86
+ {B1A2C3D4-E5F6-7890-ABCD-EF1234567891}.Release|ARM64.ActiveCfg = Release|x86
+ {B1A2C3D4-E5F6-7890-ABCD-EF1234567891}.Release|ARM64.Build.0 = Release|x86
+ {B1A2C3D4-E5F6-7890-ABCD-EF1234567891}.Release|x64.ActiveCfg = Release|x86
+ {B1A2C3D4-E5F6-7890-ABCD-EF1234567891}.Release|x64.Build.0 = Release|x86
+ {B1A2C3D4-E5F6-7890-ABCD-EF1234567891}.Release|x86.ActiveCfg = Release|x86
+ {B1A2C3D4-E5F6-7890-ABCD-EF1234567891}.Release|x86.Build.0 = Release|x86
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
diff --git a/Ink Canvas/App.xaml.cs b/Ink Canvas/App.xaml.cs
index c2d05853..4e771ab7 100644
--- a/Ink Canvas/App.xaml.cs
+++ b/Ink Canvas/App.xaml.cs
@@ -1202,6 +1202,22 @@ namespace Ink_Canvas
LogHelper.WriteLogToFile($"释放IACore DLL时出错: {ex.Message}", LogHelper.LogType.Error);
}
+ try
+ {
+ var shapeMode = ShapeRecognitionRouter.FromSettingsInt(
+ Ink_Canvas.Windows.SettingsViews.Helpers.SettingsManager.Settings?.InkToShape?.ShapeRecognitionEngine ?? 0);
+ if (!ShapeRecognitionRouter.ResolveUseWinRt(shapeMode))
+ {
+ LogHelper.WriteLogToFile("启动 IACore IPC 辅助进程");
+ bool ipcStarted = IpcIACoreClient.Instance.Start();
+ LogHelper.WriteLogToFile($"IACore IPC 辅助进程{(ipcStarted ? "启动成功" : "启动失败")}");
+ }
+ }
+ catch (Exception ex)
+ {
+ LogHelper.WriteLogToFile($"启动 IACore IPC 辅助进程时出错: {ex.Message}", LogHelper.LogType.Error);
+ }
+
try
{
LogHelper.WriteLogToFile("开始注册.icstk文件关联");
@@ -1577,6 +1593,13 @@ namespace Ink_Canvas
private void App_Exit(object sender, ExitEventArgs e)
{
CleanupTerminationMonitoring();
+
+ try
+ {
+ IpcIACoreClient.Instance.Dispose();
+ }
+ catch { }
+
// 卸载所有插件
try
{
diff --git a/Ink Canvas/Controls/BoardMenuFrame.cs b/Ink Canvas/Controls/BoardMenuFrame.cs
deleted file mode 100644
index cadefa70..00000000
--- a/Ink Canvas/Controls/BoardMenuFrame.cs
+++ /dev/null
@@ -1,141 +0,0 @@
-using System.Windows;
-using System.Windows.Controls;
-using System.Windows.Controls.Primitives;
-using System.Windows.Input;
-using System.Windows.Media;
-
-namespace Ink_Canvas.Controls
-{
- [TemplatePart(Name = PartCloseImage, Type = typeof(UIElement))]
- [TemplatePart(Name = PartAnimationRoot, Type = typeof(UIElement))]
- public class BoardMenuFrame : ContentControl
- {
- private const string PartCloseImage = "PART_CloseImage";
- private const string PartAnimationRoot = "PART_AnimationRoot";
-
- public static readonly DependencyProperty TitleProperty =
- DependencyProperty.Register(nameof(Title), typeof(object), typeof(BoardMenuFrame), new PropertyMetadata(null));
-
- public static readonly DependencyProperty TitleFontSizeProperty =
- DependencyProperty.Register(nameof(TitleFontSize), typeof(double), typeof(BoardMenuFrame), new PropertyMetadata(11d));
-
- public static readonly DependencyProperty HeaderHeightProperty =
- DependencyProperty.Register(nameof(HeaderHeight), typeof(double), typeof(BoardMenuFrame), new PropertyMetadata(48d));
-
- public static readonly DependencyProperty PanelCornerRadiusProperty =
- DependencyProperty.Register(nameof(PanelCornerRadius), typeof(CornerRadius), typeof(BoardMenuFrame), new PropertyMetadata(new CornerRadius(5)));
-
- public static readonly DependencyProperty HeaderCornerRadiusProperty =
- DependencyProperty.Register(nameof(HeaderCornerRadius), typeof(CornerRadius), typeof(BoardMenuFrame), new PropertyMetadata(new CornerRadius(6, 6, 0, 0)));
-
- public static readonly DependencyProperty PanelBackgroundProperty =
- DependencyProperty.Register(nameof(PanelBackground), typeof(Brush), typeof(BoardMenuFrame), new PropertyMetadata(null));
-
- public static readonly DependencyProperty HeaderBackgroundProperty =
- DependencyProperty.Register(nameof(HeaderBackground), typeof(Brush), typeof(BoardMenuFrame),
- new PropertyMetadata(new SolidColorBrush((Color)ColorConverter.ConvertFromString("#2563eb"))));
-
- public static readonly DependencyProperty HeaderBorderBrushProperty =
- DependencyProperty.Register(nameof(HeaderBorderBrush), typeof(Brush), typeof(BoardMenuFrame),
- new PropertyMetadata(new SolidColorBrush((Color)ColorConverter.ConvertFromString("#1e3a8a"))));
-
- public static readonly DependencyProperty IsOpenProperty =
- DependencyProperty.Register(nameof(IsOpen), typeof(bool), typeof(BoardMenuFrame), new PropertyMetadata(false));
-
- public static readonly DependencyProperty PlacementTargetProperty =
- DependencyProperty.Register(nameof(PlacementTarget), typeof(UIElement), typeof(BoardMenuFrame), new PropertyMetadata(null));
-
- public static readonly DependencyProperty PlacementProperty =
- DependencyProperty.Register(nameof(Placement), typeof(PlacementMode), typeof(BoardMenuFrame), new PropertyMetadata(PlacementMode.Custom));
-
- public static readonly DependencyProperty CustomPopupPlacementCallbackProperty =
- DependencyProperty.Register(nameof(CustomPopupPlacementCallback), typeof(CustomPopupPlacementCallback), typeof(BoardMenuFrame),
- new PropertyMetadata((CustomPopupPlacementCallback)PlaceCenteredAbove));
-
- private static CustomPopupPlacement[] PlaceCenteredAbove(Size popupSize, Size targetSize, Point offset)
- {
- return new[]
- {
- new CustomPopupPlacement(
- new Point((targetSize.Width - popupSize.Width) / 2 + offset.X,
- -popupSize.Height + offset.Y),
- PopupPrimaryAxis.Horizontal),
- new CustomPopupPlacement(
- new Point((targetSize.Width - popupSize.Width) / 2 + offset.X,
- targetSize.Height - offset.Y),
- PopupPrimaryAxis.Horizontal)
- };
- }
-
- public static readonly DependencyProperty PopupHorizontalOffsetProperty =
- DependencyProperty.Register(nameof(PopupHorizontalOffset), typeof(double), typeof(BoardMenuFrame), new PropertyMetadata(0d));
-
- public static readonly DependencyProperty PopupVerticalOffsetProperty =
- DependencyProperty.Register(nameof(PopupVerticalOffset), typeof(double), typeof(BoardMenuFrame), new PropertyMetadata(-4d));
-
- public object Title { get => GetValue(TitleProperty); set => SetValue(TitleProperty, value); }
- public double TitleFontSize { get => (double)GetValue(TitleFontSizeProperty); set => SetValue(TitleFontSizeProperty, value); }
- public double HeaderHeight { get => (double)GetValue(HeaderHeightProperty); set => SetValue(HeaderHeightProperty, value); }
- public CornerRadius PanelCornerRadius { get => (CornerRadius)GetValue(PanelCornerRadiusProperty); set => SetValue(PanelCornerRadiusProperty, value); }
- public CornerRadius HeaderCornerRadius { get => (CornerRadius)GetValue(HeaderCornerRadiusProperty); set => SetValue(HeaderCornerRadiusProperty, value); }
- public Brush PanelBackground { get => (Brush)GetValue(PanelBackgroundProperty); set => SetValue(PanelBackgroundProperty, value); }
- public Brush HeaderBackground { get => (Brush)GetValue(HeaderBackgroundProperty); set => SetValue(HeaderBackgroundProperty, value); }
- public Brush HeaderBorderBrush { get => (Brush)GetValue(HeaderBorderBrushProperty); set => SetValue(HeaderBorderBrushProperty, value); }
- public bool IsOpen { get => (bool)GetValue(IsOpenProperty); set => SetValue(IsOpenProperty, value); }
- public UIElement PlacementTarget { get => (UIElement)GetValue(PlacementTargetProperty); set => SetValue(PlacementTargetProperty, value); }
- public PlacementMode Placement { get => (PlacementMode)GetValue(PlacementProperty); set => SetValue(PlacementProperty, value); }
- public CustomPopupPlacementCallback CustomPopupPlacementCallback
- {
- get => (CustomPopupPlacementCallback)GetValue(CustomPopupPlacementCallbackProperty);
- set => SetValue(CustomPopupPlacementCallbackProperty, value);
- }
- public double PopupHorizontalOffset { get => (double)GetValue(PopupHorizontalOffsetProperty); set => SetValue(PopupHorizontalOffsetProperty, value); }
- public double PopupVerticalOffset { get => (double)GetValue(PopupVerticalOffsetProperty); set => SetValue(PopupVerticalOffsetProperty, value); }
-
- public event MouseButtonEventHandler CloseMouseDown;
- public event MouseButtonEventHandler CloseMouseUp;
-
- public UIElement AnimationTarget { get; private set; }
-
- private UIElement _closeImage;
-
- static BoardMenuFrame()
- {
- DefaultStyleKeyProperty.OverrideMetadata(typeof(BoardMenuFrame), new FrameworkPropertyMetadata(typeof(BoardMenuFrame)));
- VisibilityProperty.OverrideMetadata(typeof(BoardMenuFrame),
- new FrameworkPropertyMetadata(Visibility.Collapsed, OnVisibilityChanged));
- }
-
- private static void OnVisibilityChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
- {
- ((BoardMenuFrame)d).IsOpen = (Visibility)e.NewValue == Visibility.Visible;
- }
-
- public override void OnApplyTemplate()
- {
- base.OnApplyTemplate();
- if (_closeImage != null)
- {
- _closeImage.MouseDown -= CloseImage_MouseDown;
- _closeImage.MouseUp -= CloseImage_MouseUp;
- }
- _closeImage = GetTemplateChild(PartCloseImage) as UIElement;
- if (_closeImage != null)
- {
- _closeImage.MouseDown += CloseImage_MouseDown;
- _closeImage.MouseUp += CloseImage_MouseUp;
- }
- AnimationTarget = GetTemplateChild(PartAnimationRoot) as UIElement;
- }
-
- private void CloseImage_MouseDown(object sender, MouseButtonEventArgs e)
- {
- CloseMouseDown?.Invoke(sender, e);
- }
-
- private void CloseImage_MouseUp(object sender, MouseButtonEventArgs e)
- {
- CloseMouseUp?.Invoke(sender, e);
- }
- }
-}
\ No newline at end of file
diff --git a/Ink Canvas/Helpers/AnimationsHelper.cs b/Ink Canvas/Helpers/AnimationsHelper.cs
index 076bd4fd..c7a36a8c 100644
--- a/Ink Canvas/Helpers/AnimationsHelper.cs
+++ b/Ink Canvas/Helpers/AnimationsHelper.cs
@@ -1,4 +1,3 @@
-using Ink_Canvas.Controls;
using System;
using System.Windows;
using System.Windows.Media;
@@ -8,15 +7,7 @@ namespace Ink_Canvas.Helpers
{
internal class AnimationsHelper
{
- private static UIElement ResolveAnimationTarget(UIElement element)
- {
- if (element is BoardMenuFrame frame)
- {
- frame.ApplyTemplate();
- return frame.AnimationTarget ?? element;
- }
- return element;
- }
+ private static UIElement ResolveAnimationTarget(UIElement element) => element;
public static void ShowWithFadeIn(UIElement element, double duration = 0.15)
{
diff --git a/Ink Canvas/Helpers/InkRecognitionManager.cs b/Ink Canvas/Helpers/InkRecognitionManager.cs
index 8c30d975..40b794ab 100644
--- a/Ink Canvas/Helpers/InkRecognitionManager.cs
+++ b/Ink Canvas/Helpers/InkRecognitionManager.cs
@@ -96,8 +96,10 @@ namespace Ink_Canvas.Helpers
return RecognizeShapeWinRtOnDispatcherContext(strokes);
}
- var legacy = InkRecognizeHelper.RecognizeShapeIACore(strokes);
- return Task.FromResult(InkRecognizeHelper.FromIACoreOrEmpty(legacy));
+ // IACore 必须走 IPC 辅助进程(x86/.NET 4.7.2)。
+ // 在 .NET 6 x64 主进程中本地加载 IAWinFX 会失败,故不再本地回退。
+ var ipcResult = IpcIACoreClient.Instance.Recognize(strokes);
+ return Task.FromResult(ipcResult);
}
catch (Exception ex)
{
@@ -214,9 +216,11 @@ namespace Ink_Canvas.Helpers
public string GetSystemInfo()
{
- return _isModernSystemAvailable
- ? $"现代化墨迹识别系统 (Windows Runtime API) - 进程架构: {Environment.Is64BitProcess}"
- : $"传统墨迹识别系统 (IACore) - 进程架构: {Environment.Is64BitProcess}";
+ if (_isModernSystemAvailable)
+ return $"现代化墨迹识别系统 (Windows Runtime API) - 进程架构: {Environment.Is64BitProcess}";
+ if (IpcIACoreClient.Instance.IsAvailable)
+ return $"传统墨迹识别系统 (IACore via IPC) - 进程架构: {Environment.Is64BitProcess}";
+ return $"传统墨迹识别系统 (IACore 本地) - 进程架构: {Environment.Is64BitProcess}";
}
public void Dispose()
diff --git a/Ink Canvas/Helpers/InkRecognizeHelper.cs b/Ink Canvas/Helpers/InkRecognizeHelper.cs
index ed56e693..35d5be84 100644
--- a/Ink Canvas/Helpers/InkRecognizeHelper.cs
+++ b/Ink Canvas/Helpers/InkRecognizeHelper.cs
@@ -1,79 +1,15 @@
-using System.Linq;
using System.Threading.Tasks;
-using System.Windows;
using System.Windows.Ink;
-using System.Windows.Media;
namespace Ink_Canvas.Helpers
{
+ ///
+ /// 墨迹形状/手写识别的对外门面。
+ /// IACore 路径通过 IPC 调用 x86 辅助进程;WinRT 路径在主进程内直接调用。
+ /// 主进程 (.NET 6 x64) 不再直接引用 IAWinFX 类型。
+ ///
public class InkRecognizeHelper
{
- /// IACore / IAWinFX 形状识别(典型用于 32 位进程)。
- public static ShapeRecognizeResult RecognizeShapeIACore(StrokeCollection strokes)
- {
- if (strokes == null || strokes.Count == 0)
- return default;
-
- var analyzer = new InkAnalyzer();
- analyzer.AddStrokes(strokes);
- analyzer.SetStrokesType(strokes, StrokeType.Drawing);
-
- AnalysisAlternate analysisAlternate = null;
- int strokesCount = strokes.Count;
- var sfsaf = analyzer.Analyze();
- if (sfsaf.Successful)
- {
- var alternates = analyzer.GetAlternates();
- if (alternates.Count > 0)
- {
- while (strokesCount >= 2)
- {
- var alt0 = alternates[0];
- if (alt0?.AlternateNodes == null || alt0.AlternateNodes.Count == 0)
- break;
- var drawNode = alt0.AlternateNodes[0] as InkDrawingNode;
- if (drawNode == null)
- break;
- var shapeOk = IsContainShapeType(drawNode.GetShapeName());
- if (alt0.Strokes.Contains(strokes.Last()) && shapeOk)
- break;
- analyzer.RemoveStroke(strokes[strokes.Count - strokesCount]);
- strokesCount--;
- sfsaf = analyzer.Analyze();
- if (sfsaf.Successful)
- alternates = analyzer.GetAlternates();
- else
- break;
- if (alternates.Count == 0)
- break;
- }
- if (alternates.Count > 0)
- {
- var altFinal = alternates[0];
- if (altFinal?.AlternateNodes != null && altFinal.AlternateNodes.Count > 0)
- analysisAlternate = altFinal;
- }
- }
- }
-
- analyzer.Dispose();
-
- if (analysisAlternate != null && analysisAlternate.AlternateNodes != null && analysisAlternate.AlternateNodes.Count > 0)
- {
- var node = analysisAlternate.AlternateNodes[0] as InkDrawingNode;
- if (node == null)
- return default;
- return new ShapeRecognizeResult(node.Centroid, node.HotPoints, analysisAlternate, node);
- }
-
- return default;
- }
-
- /// 兼容旧调用:等价于 。
- public static ShapeRecognizeResult RecognizeShape(StrokeCollection strokes) =>
- RecognizeShapeIACore(strokes);
-
- /// 按设置选择 WinRT()或 IACore;WinRT 请用 。
public static InkShapeRecognitionResult RecognizeShapeUnified(
StrokeCollection strokes,
ShapeRecognitionEngineMode mode)
@@ -84,11 +20,9 @@ namespace Ink_Canvas.Helpers
if (ShapeRecognitionRouter.ResolveUseWinRt(mode))
return InkShapeRecognitionResult.Empty;
- var legacy = RecognizeShapeIACore(strokes);
- return FromIACoreOrEmpty(legacy);
+ return IpcIACoreClient.Instance.Recognize(strokes);
}
- /// 与 CE 反编译版 InkRecognitionManager.RecognizeShapeAsync 对齐的统一入口。
public static Task RecognizeShapeUnifiedAsync(
StrokeCollection strokes,
ShapeRecognitionEngineMode mode)
@@ -109,7 +43,9 @@ namespace Ink_Canvas.Helpers
WinRtHandwritingRecognizer.Warmup();
}
else
- RecognizeShapeIACore(new StrokeCollection());
+ {
+ IpcIACoreClient.Instance.Start();
+ }
}
catch
{
@@ -117,13 +53,11 @@ namespace Ink_Canvas.Helpers
}
}
- /// WinRT 手写识别(Windows 10+)。
public static Task RecognizeHandwritingUnifiedAsync(
StrokeCollection strokes,
ShapeRecognitionEngineMode mode) =>
InkRecognitionManager.Instance.RecognizeHandwritingAsync(strokes, mode);
- /// WinRT 下将识别成功的词替换为手写体字形墨迹;是否应用由设置「WinRT 识别转手写体字形」控制。
public static Task CorrectHandwritingStrokesUnifiedAsync(
StrokeCollection strokes,
ShapeRecognitionEngineMode mode) =>
@@ -133,7 +67,6 @@ namespace Ink_Canvas.Helpers
MainWindow.Settings?.InkToShape?.EnableWinRtHandwritingStrokeBeautify ?? false,
MainWindow.Settings?.InkToShape?.HandwritingCorrectionFontFamily);
- /// 显式指定是否应用手写体字形替换(忽略开关);字体仍从设置读取。
public static Task CorrectHandwritingStrokesUnifiedAsync(
StrokeCollection strokes,
ShapeRecognitionEngineMode mode,
@@ -144,53 +77,18 @@ namespace Ink_Canvas.Helpers
applyHandwritingBeautify,
MainWindow.Settings?.InkToShape?.HandwritingCorrectionFontFamily);
- internal static InkShapeRecognitionResult FromIACoreOrEmpty(ShapeRecognizeResult legacy)
- {
- if (legacy?.InkDrawingNode == null)
- return InkShapeRecognitionResult.Empty;
-
- var node = legacy.InkDrawingNode;
- var shape = node.GetShape();
- if (shape == null)
- return InkShapeRecognitionResult.Empty;
-
- var hot = ClonePointCollection(node.HotPoints);
- return new InkShapeRecognitionResult(
- node.GetShapeName(),
- legacy.Centroid,
- hot,
- shape.Width,
- shape.Height,
- node.Strokes);
- }
-
- private static PointCollection ClonePointCollection(PointCollection src)
- {
- var dst = new PointCollection();
- if (src == null) return dst;
- foreach (System.Windows.Point p in src)
- dst.Add(p);
- return dst;
- }
-
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")
- || name.Contains("Ellipse"))
- {
- return true;
- }
- return false;
+ return name.Contains("Triangle") || name.Contains("Circle") ||
+ name.Contains("Rectangle") || name.Contains("Diamond") ||
+ name.Contains("Parallelogram") || name.Contains("Square") ||
+ name.Contains("Ellipse");
}
}
- //Recognizer 的实现
-
public enum RecognizeLanguage
{
SimplifiedChinese = 0x0804,
@@ -198,127 +96,17 @@ namespace Ink_Canvas.Helpers
English = 0x0809
}
- public class ShapeRecognizeResult
- {
- public ShapeRecognizeResult(Point centroid, PointCollection hotPoints, AnalysisAlternate analysisAlternate, InkDrawingNode node)
- {
- Centroid = centroid;
- HotPoints = hotPoints;
- AnalysisAlternate = analysisAlternate;
- InkDrawingNode = node;
- }
-
- public AnalysisAlternate AnalysisAlternate { get; }
-
- public Point Centroid { get; set; }
-
- public PointCollection HotPoints { get; }
-
- public InkDrawingNode InkDrawingNode { get; }
- }
-
- ///
- /// 图形识别类
- ///
- //public class ShapeRecogniser
- //{
- // public InkAnalyzer _inkAnalyzer = null;
-
- // private ShapeRecogniser()
- // {
- // this._inkAnalyzer = new InkAnalyzer
- // {
- // AnalysisModes = AnalysisModes.AutomaticReconciliationEnabled
- // };
- // }
-
- // ///
- // /// 根据笔迹集合返回图形名称字符串
- // ///
- // ///
- // ///
- // public InkDrawingNode Recognition(StrokeCollection strokeCollection)
- // {
- // if (strokeCollection == null)
- // {
- // //MessageBox.Show("dddddd");
- // return null;
- // }
-
- // InkDrawingNode result = null;
- // try
- // {
- // this._inkAnalyzer.AddStrokes(strokeCollection);
- // if (this._inkAnalyzer.Analyze().Successful)
- // {
- // result = _internalAnalyzer(this._inkAnalyzer);
- // this._inkAnalyzer.RemoveStrokes(strokeCollection);
- // }
- // }
- // catch (System.Exception ex)
- // {
- // //result = ex.Message;
- // System.Diagnostics.Debug.WriteLine(ex.Message);
- // }
-
- // return result;
- // }
-
- // ///
- // /// 实现笔迹的分析,返回图形对应的字符串
- // /// 你在实际的应用中根据返回的字符串来生成对应的Shape
- // ///
- // ///
- // ///
- // private InkDrawingNode _internalAnalyzer(InkAnalyzer ink)
- // {
- // try
- // {
- // ContextNodeCollection nodecollections = ink.FindNodesOfType(ContextNodeType.InkDrawing);
- // foreach (ContextNode node in nodecollections)
- // {
- // InkDrawingNode drawingNode = node as InkDrawingNode;
- // if (drawingNode != null)
- // {
- // return drawingNode;//.GetShapeName();
- // }
- // }
- // }
- // catch (System.Exception ex)
- // {
- // System.Diagnostics.Debug.WriteLine(ex.Message);
- // }
-
- // return null;
- // }
-
-
- // private static ShapeRecogniser instance = null;
- // public static ShapeRecogniser Instance
- // {
- // get
- // {
- // return instance == null ? (instance = new ShapeRecogniser()) : instance;
- // }
- // }
- //}
-
-
- //用于自动控制其他形状相对于圆的位置
-
public class Circle
{
- public Circle(Point centroid, double r, Stroke stroke)
+ public Circle(System.Windows.Point centroid, double r, Stroke stroke)
{
Centroid = centroid;
R = r;
Stroke = stroke;
}
- public Point Centroid { get; set; }
-
+ public System.Windows.Point Centroid { get; set; }
public double R { get; set; }
-
public Stroke Stroke { get; set; }
}
-}
+}
\ No newline at end of file
diff --git a/Ink Canvas/Helpers/IpcIACoreClient.cs b/Ink Canvas/Helpers/IpcIACoreClient.cs
new file mode 100644
index 00000000..e320b80a
--- /dev/null
+++ b/Ink Canvas/Helpers/IpcIACoreClient.cs
@@ -0,0 +1,255 @@
+using System;
+using System.Diagnostics;
+using System.IO;
+using System.IO.Pipes;
+using System.Threading;
+using System.Windows;
+using System.Windows.Ink;
+using System.Windows.Media;
+
+namespace Ink_Canvas.Helpers
+{
+ public sealed class IpcIACoreClient : IDisposable
+ {
+ private static IpcIACoreClient _instance;
+ private static readonly object _instanceLock = new object();
+
+ public static IpcIACoreClient Instance
+ {
+ get
+ {
+ if (_instance == null)
+ lock (_instanceLock)
+ if (_instance == null)
+ _instance = new IpcIACoreClient();
+ return _instance;
+ }
+ }
+
+ private Process _helperProcess;
+ private readonly object _pipeLock = new object();
+ private bool _disposed;
+ private bool _available;
+
+ private static string HelperExePath =>
+ Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "InkCanvasForClass.IACoreHelper.exe");
+
+ private string PipeName =>
+ string.Format("ICC_IACoreHelper_{0}", Process.GetCurrentProcess().Id);
+
+ private IpcIACoreClient() { }
+
+ public bool Start()
+ {
+ if (_disposed) return false;
+ if (IsAvailable) return true;
+
+ if (!File.Exists(HelperExePath))
+ {
+ _available = false;
+ return false;
+ }
+
+ return LaunchHelper();
+ }
+
+ public bool IsAvailable => _available && _helperProcess != null && !_helperProcess.HasExited;
+
+ public InkShapeRecognitionResult Recognize(StrokeCollection strokes)
+ {
+ if (strokes == null || strokes.Count == 0)
+ return InkShapeRecognitionResult.Empty;
+
+ EnsureHelperAlive();
+ if (!IsAvailable)
+ return InkShapeRecognitionResult.Empty;
+
+ lock (_pipeLock)
+ {
+ try
+ {
+ return SendRecognizeRequest(strokes);
+ }
+ catch
+ {
+ KillHelper();
+ return InkShapeRecognitionResult.Empty;
+ }
+ }
+ }
+
+ private bool LaunchHelper()
+ {
+ try
+ {
+ KillHelper();
+
+ var psi = new ProcessStartInfo
+ {
+ FileName = HelperExePath,
+ Arguments = Process.GetCurrentProcess().Id.ToString(),
+ UseShellExecute = false,
+ CreateNoWindow = true,
+ WorkingDirectory = AppDomain.CurrentDomain.BaseDirectory
+ };
+ _helperProcess = Process.Start(psi);
+ if (_helperProcess == null)
+ {
+ _available = false;
+ return false;
+ }
+ _helperProcess.EnableRaisingEvents = true;
+ _helperProcess.Exited += OnHelperExited;
+
+ bool pipeReady = WaitForPipe(3000);
+ _available = pipeReady;
+ return pipeReady;
+ }
+ catch
+ {
+ _available = false;
+ return false;
+ }
+ }
+
+ private bool WaitForPipe(int timeoutMs)
+ {
+ int elapsed = 0;
+ while (elapsed < timeoutMs)
+ {
+ if (_helperProcess == null || _helperProcess.HasExited)
+ return false;
+
+ try
+ {
+ using (var probe = new NamedPipeClientStream(".", PipeName, PipeDirection.InOut))
+ {
+ probe.Connect(200);
+ return true;
+ }
+ }
+ catch
+ {
+ Thread.Sleep(100);
+ elapsed += 300;
+ }
+ }
+ return false;
+ }
+
+ private InkShapeRecognitionResult SendRecognizeRequest(StrokeCollection strokes)
+ {
+ using (var client = new NamedPipeClientStream(".", PipeName, PipeDirection.InOut))
+ {
+ client.Connect(IpcTimeoutMs);
+
+ using (var writer = new BinaryWriter(client, System.Text.Encoding.UTF8, leaveOpen: true))
+ using (var reader = new BinaryReader(client, System.Text.Encoding.UTF8, leaveOpen: true))
+ {
+ writer.Write(CmdRecognize);
+ writer.Write(strokes.Count);
+ foreach (var stroke in strokes)
+ {
+ var pts = stroke.StylusPoints;
+ writer.Write(pts.Count);
+ foreach (var pt in pts)
+ {
+ writer.Write((float)pt.X);
+ writer.Write((float)pt.Y);
+ writer.Write(pt.PressureFactor);
+ }
+ }
+ writer.Flush();
+
+ bool success = reader.ReadBoolean();
+ string shape = reader.ReadString();
+ float cx = reader.ReadSingle();
+ float cy = reader.ReadSingle();
+ float width = reader.ReadSingle();
+ float height = reader.ReadSingle();
+
+ int hotLen = reader.ReadInt32();
+ var hotPoints = new PointCollection();
+ for (int i = 0; i < hotLen; i++)
+ hotPoints.Add(new Point(reader.ReadSingle(), reader.ReadSingle()));
+
+ int idxLen = reader.ReadInt32();
+ var indices = new int[idxLen];
+ for (int i = 0; i < idxLen; i++)
+ indices[i] = reader.ReadInt32();
+
+ if (!success || string.IsNullOrEmpty(shape))
+ return InkShapeRecognitionResult.Empty;
+
+ var recognized = new StrokeCollection();
+ foreach (int idx in indices)
+ if (idx >= 0 && idx < strokes.Count)
+ recognized.Add(strokes[idx]);
+
+ return new InkShapeRecognitionResult(
+ shape,
+ new Point(cx, cy),
+ hotPoints,
+ width,
+ height,
+ recognized);
+ }
+ }
+ }
+
+ private void EnsureHelperAlive()
+ {
+ if (!IsAvailable)
+ LaunchHelper();
+ }
+
+ private void OnHelperExited(object sender, EventArgs e)
+ {
+ _available = false;
+ }
+
+ private void KillHelper()
+ {
+ if (_helperProcess == null) return;
+ try
+ {
+ try { _helperProcess.Exited -= OnHelperExited; } catch { }
+
+ if (!_helperProcess.HasExited)
+ {
+ try
+ {
+ using (var client = new NamedPipeClientStream(".", PipeName, PipeDirection.InOut))
+ {
+ client.Connect(500);
+ using (var w = new BinaryWriter(client))
+ w.Write(CmdShutdown);
+ }
+ }
+ catch { }
+
+ if (!_helperProcess.WaitForExit(800))
+ _helperProcess.Kill();
+ }
+ }
+ catch { }
+ finally
+ {
+ _helperProcess?.Dispose();
+ _helperProcess = null;
+ _available = false;
+ }
+ }
+
+ public void Dispose()
+ {
+ if (_disposed) return;
+ _disposed = true;
+ KillHelper();
+ }
+
+ private const int IpcTimeoutMs = 5000;
+ private const byte CmdRecognize = 0x01;
+ private const byte CmdShutdown = 0xFF;
+ }
+}
\ No newline at end of file
diff --git a/Ink Canvas/InkCanvasForClass.csproj b/Ink Canvas/InkCanvasForClass.csproj
index 92c42f06..b545447e 100644
--- a/Ink Canvas/InkCanvasForClass.csproj
+++ b/Ink Canvas/InkCanvasForClass.csproj
@@ -101,20 +101,6 @@
x64
false
-
-
- .\IACore.dll
- False
-
-
- .\IALoader.dll
- False
-
-
- .\IAWinFX.dll
- False
-
-
@@ -153,6 +139,10 @@
+
+ false
+ false
+
@@ -668,4 +658,11 @@
+
+
+
+
+
+
+
diff --git a/Ink Canvas/MainWindow.xaml b/Ink Canvas/MainWindow.xaml
index a91db569..02aa2dac 100644
--- a/Ink Canvas/MainWindow.xaml
+++ b/Ink Canvas/MainWindow.xaml
@@ -1089,137 +1089,137 @@
Label="{i18n:I18n Key=Board_Background}"
IconGeometry="F0 M24,24z M0,0z M4.71815,3.98345C6.64142,2.23541 9.19629,1.17001 12,1.17001 17.9812,1.17001 22.83,6.01877 22.83,12 22.83,17.9813 17.9812,22.83 12,22.83 11.6262,22.83 11.2568,22.8111 10.8927,22.7741 5.8167,22.2586 1.77699,18.2377 1.2325,13.1703 1.22536,13.1039 1.21882,13.0373 1.21289,12.9705 1.20871,12.9234 1.20483,12.8762 1.20125,12.8289 1.18054,12.5553 1.17,12.2789 1.17,12 1.17,9.41057 2.07878,7.03339 3.59479,5.17001 3.9391,4.74681 4.31473,4.35011 4.71815,3.98345z M12,20.83C16.8767,20.83 20.83,16.8767 20.83,12 20.83,7.12334 16.8767,3.17001 12,3.17001L12,20.83z"
ButtonMouseUp="BoardChangeBackgroundColorBtn_MouseUp" />
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+ Foreground="{DynamicResource TextForeground}"
+ FontSize="10" FontWeight="Bold"
+ HorizontalAlignment="Center"
+ Margin="0,4,0,8" />
+ Width="60" Height="30"
+ Background="#2563eb"
+ CornerRadius="4"
+ Margin="0,0,8,0"
+ MouseUp="WhiteboardModeBtn_MouseUp">
+ Foreground="White"
+ HorizontalAlignment="Center"
+ VerticalAlignment="Center" />
+ Width="60" Height="30"
+ Background="LightGray"
+ CornerRadius="4"
+ MouseUp="BlackboardModeBtn_MouseUp">
+ Foreground="Black"
+ HorizontalAlignment="Center"
+ VerticalAlignment="Center" />
+ Background="{DynamicResource SettingsPageBorderBrush}"
+ Margin="0,12,0,12" />
+ Foreground="{DynamicResource TextForeground}"
+ FontSize="10" FontWeight="Bold"
+ HorizontalAlignment="Center"
+ Margin="0,4,0,8" />
+ Width="100" Height="40"
+ BorderThickness="1"
+ BorderBrush="{DynamicResource SettingsPageBorderBrush}"
+ Background="White"
+ CornerRadius="4"
+ Margin="0,0,0,10"
+ HorizontalAlignment="Center" />
-
+ Foreground="{DynamicResource TextForeground}" />
+
+ Text="255" Width="30"
+ VerticalAlignment="Center"
+ TextAlignment="Right"
+ Foreground="{DynamicResource TextForeground}" />
-
+ Foreground="{DynamicResource TextForeground}" />
+
+ Text="255" Width="30"
+ VerticalAlignment="Center"
+ TextAlignment="Right"
+ Foreground="{DynamicResource TextForeground}" />
-
+ Foreground="{DynamicResource TextForeground}" />
+
+ Text="255" Width="30"
+ VerticalAlignment="Center"
+ TextAlignment="Right"
+ Foreground="{DynamicResource TextForeground}" />
+ Content="应用颜色"
+ Margin="0,10,0,0"
+ Padding="10,5,10,5"
+ Background="#2563eb"
+ Foreground="White"
+ BorderThickness="0"
+ HorizontalAlignment="Center"
+ Click="ApplyBackgroundColorBtn_Click" />
-
-
-
-
+
+
+
+
@@ -1234,18 +1234,11 @@
Label="{i18n:I18n Key=Board_Pen}"
IconGeometry="F1 M24,24z M0,0z M20.4786,1.42438C19.9985,1.23743 19.4847,1.15194 18.9698,1.17319 18.4549,1.19444 17.9499,1.32197 17.4869,1.54789 17.0368,1.76752 16.6358,2.07554 16.3083,2.45361L3.85516,14.9067 9.08243,20.134 21.5311,7.68529C21.9113,7.36382 22.223,6.96912 22.447,6.52438 22.6786,6.06462 22.8113,5.56167 22.8365,5.04763 22.8616,4.5336 22.7787,4.02012 22.593,3.54002 22.4073,3.05994 22.1232,2.62403 21.759,2.25988 21.3949,1.89574 20.9587,1.61132 20.4786,1.42438z M7.28056,21.1605L2.8286,16.7086 1.15912,22.83 7.28056,21.1605z"
ButtonMouseUp="PenIcon_Click" />
+
-
-
-
-
-
-
-
-
-
@@ -1560,17 +1553,10 @@
Label="{i18n:I18n Key=Board_Eraser}"
IconGeometry="F1 M24,24z M0,0z M15.6314,20.7262L22.7921,13.5655C24.3494,12.141,24.2819,9.81776,22.8105,8.34633L16.7793,2.31508C15.3547,0.757753,13.0315,0.825236,11.5601,2.29666L4.38099,9.47574 15.6314,20.7262z M14.2172,22.1404L2.96677,10.89 1.20761,12.6491C-0.34971,14.0737,-0.281711,16.3974,1.18971,17.8688L6.15089,22.83 13.5276,22.83 14.2172,22.1404z"
ButtonMouseUp="BoardEraserIcon_Click" />
+
-
-
-
-
-
-
-
-
-
+
-
+
@@ -1911,76 +1898,97 @@
Label="{i18n:I18n Key=Board_InsertImage}"
IconGeometry="F1 M24,24z M0,0z M19,3H5C3.9,3 3,3.9 3,5v14c0,1.1 0.9,2 2,2h14c1.1,0 2-0.9 2-2V5C21,3.9 20.1,3 19,3zM19,19H5V5h14V19z M17,7c-1.1,0-2,0.9-2,2s0.9,2 2,2 2-0.9 2-2S18.1,7 17,7zM7,17l2.5-3.01 1.96,2.36 2.54-3.21L17,17H7z"
ButtonMouseUp="InsertImageOptions_MouseUp" />
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
+
diff --git a/Ink Canvas/Properties/Strings.resx b/Ink Canvas/Properties/Strings.resx
index 594af26d..03da96e4 100644
--- a/Ink Canvas/Properties/Strings.resx
+++ b/Ink Canvas/Properties/Strings.resx
@@ -589,7 +589,7 @@
识别引擎
- 自动:64 位进程使用 WinRT(Windows 10+),32 位使用 IACore。可强制指定 IACore 或 WinRT。
+ 自动:在 Windows 10+ 使用 WinRT,否则使用 IACore。可强制指定 IACore 或 WinRT。IACore 通过 IPC 辅助进程运行,在 32/64 位主进程下均可用。
自动
diff --git a/Ink Canvas/Themes/Generic.xaml b/Ink Canvas/Themes/Generic.xaml
deleted file mode 100644
index 5e99e216..00000000
--- a/Ink Canvas/Themes/Generic.xaml
+++ /dev/null
@@ -1,61 +0,0 @@
-
-
-
\ No newline at end of file
diff --git a/InkCanvasForClass.IACoreHelper/App.config b/InkCanvasForClass.IACoreHelper/App.config
new file mode 100644
index 00000000..1dc0cb08
--- /dev/null
+++ b/InkCanvasForClass.IACoreHelper/App.config
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/InkCanvasForClass.IACoreHelper/InkCanvasForClass.IACoreHelper.csproj b/InkCanvasForClass.IACoreHelper/InkCanvasForClass.IACoreHelper.csproj
new file mode 100644
index 00000000..96bb9909
--- /dev/null
+++ b/InkCanvasForClass.IACoreHelper/InkCanvasForClass.IACoreHelper.csproj
@@ -0,0 +1,56 @@
+
+
+
+
+ Debug
+ x86
+ {B1A2C3D4-E5F6-7890-ABCD-EF1234567891}
+ Exe
+ InkCanvasForClass.IACoreHelper
+ InkCanvasForClass.IACoreHelper
+ v4.7.2
+ 512
+ true
+ true
+
+
+ x86
+ true
+ full
+ false
+ bin\Debug\
+ DEBUG;TRACE
+ prompt
+ 4
+
+
+ x86
+ pdbonly
+ true
+ bin\Release\
+ TRACE
+ prompt
+ 4
+
+
+
+
+
+
+
+
+
+ ..\Ink Canvas\Resources\IACore\IAWinFX.dll
+ false
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/InkCanvasForClass.IACoreHelper/IpcProtocol.cs b/InkCanvasForClass.IACoreHelper/IpcProtocol.cs
new file mode 100644
index 00000000..ed93f411
--- /dev/null
+++ b/InkCanvasForClass.IACoreHelper/IpcProtocol.cs
@@ -0,0 +1,133 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Text;
+
+namespace InkCanvasForClass.IACoreHelper
+{
+ // Named Pipe 名称,主进程和辅助进程共用
+ internal static class IpcConstants
+ {
+ public const string PipeName = "ICC_IACoreHelper_{0}"; // {0} = 主进程 PID
+ public const int RequestTimeout = 5000; // ms
+ public const byte CmdRecognize = 0x01;
+ public const byte CmdShutdown = 0xFF;
+ }
+
+ // 单个 StylusPoint 的轻量传输结构
+ internal struct StylusPointDto
+ {
+ public float X;
+ public float Y;
+ public float Pressure;
+ }
+
+ // 单条笔画
+ internal class StrokeDto
+ {
+ public StylusPointDto[] Points;
+ }
+
+ // 识别请求(主进程 → 辅助进程)
+ internal class RecognizeRequest
+ {
+ public StrokeDto[] Strokes;
+
+ public void WriteTo(BinaryWriter w)
+ {
+ w.Write(IpcConstants.CmdRecognize);
+ w.Write(Strokes.Length);
+ foreach (var stroke in Strokes)
+ {
+ w.Write(stroke.Points.Length);
+ foreach (var pt in stroke.Points)
+ {
+ w.Write(pt.X);
+ w.Write(pt.Y);
+ w.Write(pt.Pressure);
+ }
+ }
+ }
+
+ public static RecognizeRequest ReadFrom(BinaryReader r)
+ {
+ int strokeCount = r.ReadInt32();
+ var strokes = new StrokeDto[strokeCount];
+ for (int i = 0; i < strokeCount; i++)
+ {
+ int ptCount = r.ReadInt32();
+ var pts = new StylusPointDto[ptCount];
+ for (int j = 0; j < ptCount; j++)
+ pts[j] = new StylusPointDto { X = r.ReadSingle(), Y = r.ReadSingle(), Pressure = r.ReadSingle() };
+ strokes[i] = new StrokeDto { Points = pts };
+ }
+ return new RecognizeRequest { Strokes = strokes };
+ }
+ }
+
+ // 识别响应(辅助进程 → 主进程)
+ internal class RecognizeResponse
+ {
+ public bool Success;
+ public string ShapeName; // e.g. "Circle", "Rectangle", "Triangle" ...
+ public float CentroidX;
+ public float CentroidY;
+ public float ShapeWidth;
+ public float ShapeHeight;
+ public float[] HotPointsX;
+ public float[] HotPointsY;
+ public int[] StrokeIndices; // 参与识别的笔画在原始数组中的下标
+
+ public void WriteTo(BinaryWriter w)
+ {
+ w.Write(Success);
+ w.Write(ShapeName ?? string.Empty);
+ w.Write(CentroidX);
+ w.Write(CentroidY);
+ w.Write(ShapeWidth);
+ w.Write(ShapeHeight);
+
+ int hotLen = HotPointsX != null ? HotPointsX.Length : 0;
+ w.Write(hotLen);
+ for (int i = 0; i < hotLen; i++)
+ {
+ w.Write(HotPointsX[i]);
+ w.Write(HotPointsY[i]);
+ }
+
+ int idxLen = StrokeIndices != null ? StrokeIndices.Length : 0;
+ w.Write(idxLen);
+ for (int i = 0; i < idxLen; i++)
+ w.Write(StrokeIndices[i]);
+ }
+
+ public static RecognizeResponse ReadFrom(BinaryReader r)
+ {
+ var resp = new RecognizeResponse
+ {
+ Success = r.ReadBoolean(),
+ ShapeName = r.ReadString(),
+ CentroidX = r.ReadSingle(),
+ CentroidY = r.ReadSingle(),
+ ShapeWidth = r.ReadSingle(),
+ ShapeHeight = r.ReadSingle()
+ };
+
+ int hotLen = r.ReadInt32();
+ resp.HotPointsX = new float[hotLen];
+ resp.HotPointsY = new float[hotLen];
+ for (int i = 0; i < hotLen; i++)
+ {
+ resp.HotPointsX[i] = r.ReadSingle();
+ resp.HotPointsY[i] = r.ReadSingle();
+ }
+
+ int idxLen = r.ReadInt32();
+ resp.StrokeIndices = new int[idxLen];
+ for (int i = 0; i < idxLen; i++)
+ resp.StrokeIndices[i] = r.ReadInt32();
+
+ return resp;
+ }
+ }
+}
\ No newline at end of file
diff --git a/InkCanvasForClass.IACoreHelper/Program.cs b/InkCanvasForClass.IACoreHelper/Program.cs
new file mode 100644
index 00000000..640606f5
--- /dev/null
+++ b/InkCanvasForClass.IACoreHelper/Program.cs
@@ -0,0 +1,200 @@
+using System;
+using System.IO;
+using System.IO.Pipes;
+using System.Linq;
+using System.Windows.Ink;
+using System.Windows.Input;
+
+namespace InkCanvasForClass.IACoreHelper
+{
+ internal static class Program
+ {
+ [STAThread]
+ static void Main(string[] args)
+ {
+ if (args.Length < 1 || !int.TryParse(args[0], out int parentPid))
+ {
+ Console.Error.WriteLine("Usage: IACoreHelper.exe ");
+ return;
+ }
+
+ string pipeName = string.Format(IpcConstants.PipeName, parentPid);
+
+ try
+ {
+ RunPipeServer(pipeName);
+ }
+ catch (Exception ex)
+ {
+ Console.Error.WriteLine("IACoreHelper fatal: " + ex.Message);
+ }
+ }
+
+ private static void RunPipeServer(string pipeName)
+ {
+ while (true)
+ {
+ using (var server = new NamedPipeServerStream(
+ pipeName,
+ PipeDirection.InOut,
+ 1,
+ PipeTransmissionMode.Byte,
+ PipeOptions.WriteThrough))
+ {
+ server.WaitForConnection();
+
+ try
+ {
+ using (var reader = new BinaryReader(server, System.Text.Encoding.UTF8, leaveOpen: true))
+ using (var writer = new BinaryWriter(server, System.Text.Encoding.UTF8, leaveOpen: true))
+ {
+ byte cmd = reader.ReadByte();
+
+ if (cmd == IpcConstants.CmdShutdown)
+ return;
+
+ if (cmd == IpcConstants.CmdRecognize)
+ {
+ var request = RecognizeRequest.ReadFrom(reader);
+ var response = HandleRecognize(request);
+ response.WriteTo(writer);
+ writer.Flush();
+ }
+ }
+ }
+ catch (Exception ex)
+ {
+ Console.Error.WriteLine("IACoreHelper pipe error: " + ex.Message);
+ }
+ }
+ }
+ }
+
+ private static RecognizeResponse HandleRecognize(RecognizeRequest request)
+ {
+ try
+ {
+ var strokes = BuildStrokeCollection(request);
+ if (strokes.Count == 0)
+ return new RecognizeResponse { Success = false, ShapeName = string.Empty };
+
+ return RecognizeCore(strokes);
+ }
+ catch (Exception ex)
+ {
+ Console.Error.WriteLine("IACoreHelper recognize error: " + ex.Message);
+ return new RecognizeResponse { Success = false, ShapeName = string.Empty };
+ }
+ }
+
+ private static StrokeCollection BuildStrokeCollection(RecognizeRequest request)
+ {
+ var sc = new StrokeCollection();
+ foreach (var strokeDto in request.Strokes)
+ {
+ if (strokeDto.Points == null || strokeDto.Points.Length == 0)
+ continue;
+
+ var stylusPoints = new StylusPointCollection();
+ foreach (var pt in strokeDto.Points)
+ stylusPoints.Add(new StylusPoint(pt.X, pt.Y, pt.Pressure));
+
+ sc.Add(new Stroke(stylusPoints));
+ }
+ return sc;
+ }
+
+ private static RecognizeResponse RecognizeCore(StrokeCollection strokes)
+ {
+ var analyzer = new InkAnalyzer();
+ analyzer.AddStrokes(strokes);
+ analyzer.SetStrokesType(strokes, StrokeType.Drawing);
+
+ AnalysisAlternate analysisAlternate = null;
+ int strokesCount = strokes.Count;
+ var analysisStatus = analyzer.Analyze();
+
+ if (analysisStatus.Successful)
+ {
+ var alternates = analyzer.GetAlternates();
+ if (alternates.Count > 0)
+ {
+ while (strokesCount >= 2)
+ {
+ var alt0 = alternates[0];
+ if (alt0?.AlternateNodes == null || alt0.AlternateNodes.Count == 0)
+ break;
+ var drawNode = alt0.AlternateNodes[0] as InkDrawingNode;
+ if (drawNode == null)
+ break;
+ bool shapeOk = IsContainShapeType(drawNode.GetShapeName());
+ if (alt0.Strokes.Contains(strokes.Last()) && shapeOk)
+ break;
+ analyzer.RemoveStroke(strokes[strokes.Count - strokesCount]);
+ strokesCount--;
+ analysisStatus = analyzer.Analyze();
+ if (analysisStatus.Successful)
+ alternates = analyzer.GetAlternates();
+ else
+ break;
+ if (alternates.Count == 0)
+ break;
+ }
+
+ if (alternates.Count > 0)
+ {
+ var altFinal = alternates[0];
+ if (altFinal?.AlternateNodes != null && altFinal.AlternateNodes.Count > 0)
+ analysisAlternate = altFinal;
+ }
+ }
+ }
+
+ analyzer.Dispose();
+
+ if (analysisAlternate?.AlternateNodes == null || analysisAlternate.AlternateNodes.Count == 0)
+ return new RecognizeResponse { Success = false, ShapeName = string.Empty };
+
+ var node = analysisAlternate.AlternateNodes[0] as InkDrawingNode;
+ if (node == null)
+ return new RecognizeResponse { Success = false, ShapeName = string.Empty };
+
+ var shape = node.GetShape();
+ var center = node.Centroid;
+ var hot = node.HotPoints;
+
+ float[] hotX = new float[hot?.Count ?? 0];
+ float[] hotY = new float[hot?.Count ?? 0];
+ if (hot != null)
+ for (int i = 0; i < hot.Count; i++) { hotX[i] = (float)hot[i].X; hotY[i] = (float)hot[i].Y; }
+
+ var participatingStrokes = analysisAlternate.Strokes;
+ int[] strokeIndices = new int[participatingStrokes?.Count ?? 0];
+ if (participatingStrokes != null)
+ for (int i = 0; i < participatingStrokes.Count; i++)
+ strokeIndices[i] = strokes.IndexOf(participatingStrokes[i]);
+
+ return new RecognizeResponse
+ {
+ Success = true,
+ ShapeName = node.GetShapeName() ?? string.Empty,
+ CentroidX = (float)center.X,
+ CentroidY = (float)center.Y,
+ ShapeWidth = shape != null ? (float)shape.Width : 0f,
+ ShapeHeight = shape != null ? (float)shape.Height : 0f,
+ HotPointsX = hotX,
+ HotPointsY = hotY,
+ StrokeIndices = strokeIndices
+ };
+ }
+
+ private static bool IsContainShapeType(string name)
+ {
+ if (string.IsNullOrEmpty(name)) return false;
+ return name.Contains("Triangle") || name.Contains("Circle") ||
+ name.Contains("Rectangle") || name.Contains("Diamond") ||
+ name.Contains("Parallelogram") || name.Contains("Square") ||
+ name.Contains("Ellipse");
+ }
+ }
+}
\ No newline at end of file
diff --git a/InkCanvasForClass.IACoreHelper/Properties/AssemblyInfo.cs b/InkCanvasForClass.IACoreHelper/Properties/AssemblyInfo.cs
new file mode 100644
index 00000000..51bf9aa3
--- /dev/null
+++ b/InkCanvasForClass.IACoreHelper/Properties/AssemblyInfo.cs
@@ -0,0 +1,11 @@
+using System.Reflection;
+using System.Runtime.InteropServices;
+
+[assembly: AssemblyTitle("InkCanvasForClass.IACoreHelper")]
+[assembly: AssemblyDescription("IACore 32-bit ink shape recognition helper process")]
+[assembly: AssemblyCompany("ICC CE")]
+[assembly: AssemblyProduct("InkCanvasForClass.IACoreHelper")]
+[assembly: AssemblyCopyright("Copyright © ICC CE")]
+[assembly: ComVisible(false)]
+[assembly: AssemblyVersion("1.0.0.0")]
+[assembly: AssemblyFileVersion("1.0.0.0")]
\ No newline at end of file