Merge branch 'net6' into net462

This commit is contained in:
doudou0720
2026-05-02 09:08:38 +08:00
16 changed files with 1000 additions and 707 deletions
+22
View File
@@ -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
+23
View File
@@ -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
{
-141
View File
@@ -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);
}
}
}
+1 -10
View File
@@ -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)
{
+9 -5
View File
@@ -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()
+16 -228
View File
@@ -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
{
/// <summary>
/// 墨迹形状/手写识别的对外门面。
/// IACore 路径通过 IPC 调用 x86 辅助进程;WinRT 路径在主进程内直接调用。
/// 主进程 (.NET 6 x64) 不再直接引用 IAWinFX 类型。
/// </summary>
public class InkRecognizeHelper
{
/// <summary>IACore / IAWinFX 形状识别(典型用于 32 位进程)。</summary>
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;
}
/// <summary>兼容旧调用:等价于 <see cref="RecognizeShapeIACore"/>。</summary>
public static ShapeRecognizeResult RecognizeShape(StrokeCollection strokes) =>
RecognizeShapeIACore(strokes);
/// <summary>按设置选择 WinRT<see cref="InkRecognitionManager"/>)或 IACoreWinRT 请用 <see cref="RecognizeShapeUnifiedAsync"/>。</summary>
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);
}
/// <summary>与 CE 反编译版 <c>InkRecognitionManager.RecognizeShapeAsync</c> 对齐的统一入口。</summary>
public static Task<InkShapeRecognitionResult> 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
}
}
/// <summary>WinRT 手写识别(Windows 10+)。</summary>
public static Task<HandwritingRecognitionResult> RecognizeHandwritingUnifiedAsync(
StrokeCollection strokes,
ShapeRecognitionEngineMode mode) =>
InkRecognitionManager.Instance.RecognizeHandwritingAsync(strokes, mode);
/// <summary>WinRT 下将识别成功的词替换为手写体字形墨迹;是否应用由设置「WinRT 识别转手写体字形」控制。</summary>
public static Task<StrokeCollection> CorrectHandwritingStrokesUnifiedAsync(
StrokeCollection strokes,
ShapeRecognitionEngineMode mode) =>
@@ -133,7 +67,6 @@ namespace Ink_Canvas.Helpers
MainWindow.Settings?.InkToShape?.EnableWinRtHandwritingStrokeBeautify ?? false,
MainWindow.Settings?.InkToShape?.HandwritingCorrectionFontFamily);
/// <summary>显式指定是否应用手写体字形替换(忽略开关);字体仍从设置读取。</summary>
public static Task<StrokeCollection> 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; }
}
/// <summary>
/// 图形识别类
/// </summary>
//public class ShapeRecogniser
//{
// public InkAnalyzer _inkAnalyzer = null;
// private ShapeRecogniser()
// {
// this._inkAnalyzer = new InkAnalyzer
// {
// AnalysisModes = AnalysisModes.AutomaticReconciliationEnabled
// };
// }
// /// <summary>
// /// 根据笔迹集合返回图形名称字符串
// /// </summary>
// /// <param name="strokeCollection"></param>
// /// <returns></returns>
// 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;
// }
// /// <summary>
// /// 实现笔迹的分析,返回图形对应的字符串
// /// 你在实际的应用中根据返回的字符串来生成对应的Shape
// /// </summary>
// /// <param name="ink"></param>
// /// <returns></returns>
// 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; }
}
}
}
+255
View File
@@ -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;
}
}
+11 -14
View File
@@ -101,20 +101,6 @@
<PlatformTarget>x64</PlatformTarget>
<Prefer32Bit>false</Prefer32Bit>
</PropertyGroup>
<ItemGroup>
<Reference Include="IACore">
<HintPath>.\IACore.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="IALoader">
<HintPath>.\IALoader.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="IAWinFX">
<HintPath>.\IAWinFX.dll</HintPath>
<Private>False</Private>
</Reference>
</ItemGroup>
<ItemGroup>
<None Include="app.manifest" />
</ItemGroup>
@@ -153,6 +139,10 @@
<ItemGroup>
<ProjectReference Include="..\InkCanvas.PluginSdk\InkCanvas.PluginSdk.csproj" />
<ProjectReference Include="..\InkCanvas.Controls\InkCanvas.Controls.csproj" />
<ProjectReference Include="..\InkCanvasForClass.IACoreHelper\InkCanvasForClass.IACoreHelper.csproj">
<ReferenceOutputAssembly>false</ReferenceOutputAssembly>
<Private>false</Private>
</ProjectReference>
</ItemGroup>
<ItemGroup Condition="'$(MSBuildRuntimeType)' == 'Full'">
<COMReference Include="IWshRuntimeLibrary">
@@ -668,4 +658,11 @@
<Target Name="CleanTelemetryDsn" AfterTargets="Build;Clean" Condition="Exists('$(MSBuildProjectDirectory)\telemetry_dsn.generated.txt')">
<Delete Files="$(MSBuildProjectDirectory)\telemetry_dsn.generated.txt" />
</Target>
<Target Name="CopyIACoreHelper" AfterTargets="Build">
<ItemGroup>
<IACoreHelperFiles Include="$(MSBuildProjectDirectory)\..\InkCanvasForClass.IACoreHelper\bin\$(Configuration)\*.*" />
</ItemGroup>
<Copy SourceFiles="@(IACoreHelperFiles)" DestinationFolder="$(OutDir)" SkipUnchangedFiles="true" Condition="'@(IACoreHelperFiles)' != ''" />
</Target>
</Project>
+256 -247
View File
@@ -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" />
<Border>
<Grid Width="0" Margin="0,0,0,5" RenderTransformOrigin="0,1">
<Border ClipToBounds="True" x:Name="BackgroundPalette"
Visibility="Collapsed"
Margin="-115,-360,-55,50"
CornerRadius="8"
Background="{DynamicResource SettingsPageBackground}" Opacity="1" BorderBrush="#2563eb"
BorderThickness="1">
<ikw:SimpleStackPanel Margin="0">
<Border BorderBrush="#1e3a8a" BorderThickness="0,0,0,1"
CornerRadius="8,8,0,0"
Background="#2563eb" Margin="-1,-1,-1,1">
<Grid Height="24">
<TextBlock Text="背景设置" Margin="8,0,0,0" Foreground="White"
VerticalAlignment="Center" HorizontalAlignment="Left"
FontSize="11" FontWeight="Bold" />
<Image Margin="0,0,8,0"
Source="/Resources/new-icons/close-white.png"
RenderOptions.BitmapScalingMode="HighQuality"
Height="16" Width="16"
HorizontalAlignment="Right" VerticalAlignment="Center"
MouseDown="Border_MouseDown"
MouseUp="CloseBordertools_MouseUp" />
</Grid>
</Border>
<Border>
<Grid Width="0" Margin="0,0,0,5" RenderTransformOrigin="0,1">
<Border ClipToBounds="True" x:Name="BackgroundPalette"
Visibility="Collapsed" d:Visibility="Visible"
Margin="-189,-360,-55,50"
CornerRadius="8"
Background="{DynamicResource SettingsPageBackground}" Opacity="1" BorderBrush="#2563eb"
BorderThickness="1">
<ikw:SimpleStackPanel Margin="0">
<Border BorderBrush="#1e3a8a" BorderThickness="0,0,0,1"
CornerRadius="8,8,0,0"
Background="#2563eb" Margin="-1,-1,-1,1">
<Grid Height="24">
<TextBlock Text="背景设置" Margin="8,0,0,0" Foreground="White"
VerticalAlignment="Center" HorizontalAlignment="Left"
FontSize="11" FontWeight="Bold" />
<Image Margin="0,0,8,0"
Source="/Resources/new-icons/close-white.png"
RenderOptions.BitmapScalingMode="HighQuality"
Height="16" Width="16"
HorizontalAlignment="Right" VerticalAlignment="Center"
MouseDown="Border_MouseDown"
MouseUp="CloseBordertools_MouseUp" />
</Grid>
</Border>
<StackPanel>
<StackPanel Margin="8">
<TextBlock Text="白板模式"
Foreground="{DynamicResource TextForeground}"
FontSize="10" FontWeight="Bold"
HorizontalAlignment="Center"
Margin="0,4,0,8" />
Foreground="{DynamicResource TextForeground}"
FontSize="10" FontWeight="Bold"
HorizontalAlignment="Center"
Margin="0,4,0,8" />
<StackPanel Orientation="Horizontal" HorizontalAlignment="Center">
<Border x:Name="WhiteboardModeBtn"
Width="60" Height="30"
Background="#2563eb"
CornerRadius="4"
Margin="0,0,8,0"
MouseUp="WhiteboardModeBtn_MouseUp">
Width="60" Height="30"
Background="#2563eb"
CornerRadius="4"
Margin="0,0,8,0"
MouseUp="WhiteboardModeBtn_MouseUp">
<TextBlock Text="白板"
Foreground="White"
HorizontalAlignment="Center"
VerticalAlignment="Center" />
Foreground="White"
HorizontalAlignment="Center"
VerticalAlignment="Center" />
</Border>
<Border x:Name="BlackboardModeBtn"
Width="60" Height="30"
Background="LightGray"
CornerRadius="4"
MouseUp="BlackboardModeBtn_MouseUp">
Width="60" Height="30"
Background="LightGray"
CornerRadius="4"
MouseUp="BlackboardModeBtn_MouseUp">
<TextBlock Text="黑板"
Foreground="Black"
HorizontalAlignment="Center"
VerticalAlignment="Center" />
Foreground="Black"
HorizontalAlignment="Center"
VerticalAlignment="Center" />
</Border>
</StackPanel>
<Border Height="1"
Background="{DynamicResource SettingsPageBorderBrush}"
Margin="0,12,0,12" />
Background="{DynamicResource SettingsPageBorderBrush}"
Margin="0,12,0,12" />
<TextBlock Text="背景颜色"
Foreground="{DynamicResource TextForeground}"
FontSize="10" FontWeight="Bold"
HorizontalAlignment="Center"
Margin="0,4,0,8" />
Foreground="{DynamicResource TextForeground}"
FontSize="10" FontWeight="Bold"
HorizontalAlignment="Center"
Margin="0,4,0,8" />
<Border x:Name="BackgroundColorPreview"
Width="100" Height="40"
BorderThickness="1"
BorderBrush="{DynamicResource SettingsPageBorderBrush}"
Background="White"
CornerRadius="4"
Margin="0,0,0,10"
HorizontalAlignment="Center" />
Width="100" Height="40"
BorderThickness="1"
BorderBrush="{DynamicResource SettingsPageBorderBrush}"
Background="White"
CornerRadius="4"
Margin="0,0,0,10"
HorizontalAlignment="Center" />
<StackPanel Orientation="Horizontal" Margin="10,0,10,5">
<TextBlock Text="R:" Width="20"
VerticalAlignment="Center"
Foreground="{DynamicResource TextForeground}" />
<Slider x:Name="BackgroundRSlider"
Minimum="0" Maximum="255" Value="255"
Width="150" Margin="5,0,5,0"
VerticalAlignment="Center"
ValueChanged="BackgroundRSlider_ValueChanged" />
Foreground="{DynamicResource TextForeground}" />
<Slider x:Name="BackgroundRSlider"
Minimum="0" Maximum="255" Value="255"
Width="150" Margin="5,0,5,0"
VerticalAlignment="Center"
ValueChanged="BackgroundRSlider_ValueChanged" />
<TextBlock x:Name="BackgroundRValue"
Text="255" Width="30"
VerticalAlignment="Center"
TextAlignment="Right"
Foreground="{DynamicResource TextForeground}" />
Text="255" Width="30"
VerticalAlignment="Center"
TextAlignment="Right"
Foreground="{DynamicResource TextForeground}" />
</StackPanel>
<StackPanel Orientation="Horizontal" Margin="10,0,10,5">
<TextBlock Text="G:" Width="20"
VerticalAlignment="Center"
Foreground="{DynamicResource TextForeground}" />
<Slider x:Name="BackgroundGSlider"
Minimum="0" Maximum="255" Value="255"
Width="150" Margin="5,0,5,0"
VerticalAlignment="Center"
ValueChanged="BackgroundGSlider_ValueChanged" />
Foreground="{DynamicResource TextForeground}" />
<Slider x:Name="BackgroundGSlider"
Minimum="0" Maximum="255" Value="255"
Width="150" Margin="5,0,5,0"
VerticalAlignment="Center"
ValueChanged="BackgroundGSlider_ValueChanged" />
<TextBlock x:Name="BackgroundGValue"
Text="255" Width="30"
VerticalAlignment="Center"
TextAlignment="Right"
Foreground="{DynamicResource TextForeground}" />
Text="255" Width="30"
VerticalAlignment="Center"
TextAlignment="Right"
Foreground="{DynamicResource TextForeground}" />
</StackPanel>
<StackPanel Orientation="Horizontal" Margin="10,0,10,5">
<TextBlock Text="B:" Width="20"
VerticalAlignment="Center"
Foreground="{DynamicResource TextForeground}" />
<Slider x:Name="BackgroundBSlider"
Minimum="0" Maximum="255" Value="255"
Width="150" Margin="5,0,5,0"
VerticalAlignment="Center"
ValueChanged="BackgroundBSlider_ValueChanged" />
Foreground="{DynamicResource TextForeground}" />
<Slider x:Name="BackgroundBSlider"
Minimum="0" Maximum="255" Value="255"
Width="150" Margin="5,0,5,0"
VerticalAlignment="Center"
ValueChanged="BackgroundBSlider_ValueChanged" />
<TextBlock x:Name="BackgroundBValue"
Text="255" Width="30"
VerticalAlignment="Center"
TextAlignment="Right"
Foreground="{DynamicResource TextForeground}" />
Text="255" Width="30"
VerticalAlignment="Center"
TextAlignment="Right"
Foreground="{DynamicResource TextForeground}" />
</StackPanel>
<Button x:Name="ApplyBackgroundColorBtn"
Content="应用颜色"
Margin="0,10,0,0"
Padding="10,5,10,5"
Background="#2563eb"
Foreground="White"
BorderThickness="0"
HorizontalAlignment="Center"
Click="ApplyBackgroundColorBtn_Click" />
Content="应用颜色"
Margin="0,10,0,0"
Padding="10,5,10,5"
Background="#2563eb"
Foreground="White"
BorderThickness="0"
HorizontalAlignment="Center"
Click="ApplyBackgroundColorBtn_Click" />
</StackPanel>
</StackPanel>
</ikw:SimpleStackPanel>
</Border>
</Grid>
</Border>
</ikw:SimpleStackPanel>
</Border>
</Grid>
</Border>
</ikw:SimpleStackPanel>
</Border>
@@ -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" />
<!--笔菜单-->
<Border>
<Grid Margin="0,5,0,5" Width="0">
<Grid Name="BoardPenPaletteGrid" Margin="-160,-240,-53,50" RenderTransformOrigin="0,1">
<Grid.RenderTransform>
<TransformGroup>
<ScaleTransform ScaleX="1.5" ScaleY="1.5" />
<SkewTransform />
<RotateTransform />
<TranslateTransform />
</TransformGroup>
</Grid.RenderTransform>
<Border x:Name="BoardPenPalette" Visibility="Collapsed"
<Border x:Name="BoardPenPalette" Visibility="Collapsed" d:Visibility="Visible"
Background="{DynamicResource FloatBarBackground}"
Opacity="1" BorderBrush="#2563eb"
BorderThickness="1" CornerRadius="8">
@@ -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" />
<!--橡皮菜单-->
<Border>
<Grid RenderTransformOrigin="0,1" Margin="-133,-172,13,55">
<Grid.RenderTransform>
<TransformGroup>
<ScaleTransform ScaleX="1.5" ScaleY="1.5" />
<SkewTransform />
<RotateTransform />
<TranslateTransform />
</TransformGroup>
</Grid.RenderTransform>
<Border Visibility="Collapsed" ClipToBounds="True"
<Border Visibility="Collapsed" ClipToBounds="True" d:Visibility="Visible"
x:Name="BoardEraserSizePanel"
CornerRadius="5" Background="{DynamicResource FloatBarBackground}"
Opacity="1"
@@ -1593,7 +1579,7 @@
</Canvas>
</Border>
<ikw:SimpleStackPanel Orientation="Horizontal" Margin="0,6,0,0"
Spacing="-2"
Spacing="2"
VerticalAlignment="Center"
HorizontalAlignment="Center">
<Label Margin="0,0,0,0" Content="{i18n:I18n Key=Board_Size}" FontWeight="Bold"
@@ -1759,13 +1745,14 @@
Label="{i18n:I18n Key=Board_Shape}"
IconGeometry="F1 M24,24z M0,0z M17.8604,10.7597L12.0068,1.17001 6.13961,10.7597 17.8604,10.7597z M10.9345,13.2403L1.34476,13.2403 1.34476,22.83 10.9345,22.83 10.9345,13.2403z M17.8604,13.2403C15.2122,13.2403 13.0655,15.387 13.0655,18.0352 13.0655,20.6833 15.2122,22.83 17.8604,22.83 20.5085,22.83 22.6552,20.6833 22.6552,18.0352 22.6552,15.387 20.5085,13.2403 17.8604,13.2403z"
ButtonMouseUp="ImageDrawShape_MouseUp" />
<!--图形菜单-->
<Border Width="0">
<Border x:Name="BoardBorderDrawShape" Visibility="Visible"
Background="{DynamicResource FloatBarBackground}"
Opacity="1" BorderBrush="{DynamicResource FloatBarBorderBrush}" BorderThickness="1" CornerRadius="5"
Margin="-147,-286,-89,55">
<Viewbox>
<ikw:SimpleStackPanel Spacing="-8" Orientation="Vertical">
<ikw:SimpleStackPanel Spacing="8" Orientation="Vertical">
<TextBlock FontSize="16"
Foreground="{DynamicResource FloatBarForeground}"
HorizontalAlignment="Left" Margin="10,8">
@@ -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" />
<localControls:BoardMenuFrame x:Name="BoardImageOptionsPanel" Visibility="Visible" ClipToBounds="True"
PlacementTarget="{Binding ElementName=BoardInsertImage}"
PanelCornerRadius="5" HeaderCornerRadius="6,6,0,0" TitleFontSize="11"
PanelBackground="{DynamicResource FloatBarBackground}" Opacity="1"
Title="{i18n:I18n Key=Board_SelectImage}"
CloseMouseDown="Border_MouseDown"
CloseMouseUp="CloseImageOptionsPanel_MouseUp">
<ikw:SimpleStackPanel Margin="6,4,6,4" Spacing="2">
<!-- Screenshot Option -->
<Border MouseDown="Border_MouseDown" MouseUp="ImageOptionScreenshot_MouseUp"
Background="Transparent" CornerRadius="3" Padding="6,4">
<Border.Style>
<Style TargetType="Border">
<Setter Property="Background" Value="Transparent"/>
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Background" Value="#f0f9ff"/>
</Trigger>
</Style.Triggers>
</Style>
</Border.Style>
<ikw:SimpleStackPanel Orientation="Horizontal" Spacing="6">
<Image Height="16" Width="16">
<Image.Source>
<DrawingImage>
<DrawingImage.Drawing>
<DrawingGroup ClipGeometry="M0,0 V24 H24 V0 H0 Z">
<GeometryDrawing Brush="#2563eb"
Geometry="{Binding Source={x:Static icons:XamlGraphicsIconGeometries.ScreenshotIconGeometry}, Converter={StaticResource StringToGeometryConverter}}"/>
</DrawingGroup>
</DrawingImage.Drawing>
</DrawingImage>
</Image.Source>
</Image>
<TextBlock Text="{i18n:I18n Key=Board_Screenshot}" FontSize="10"
Foreground="{DynamicResource TextForeground}" VerticalAlignment="Center"/>
<!--插入图片菜单-->
<Border>
<Grid RenderTransformOrigin="0,1" Margin="-133,-172,13,55">
<Border Visibility="Collapsed" ClipToBounds="True" d:Visibility="Visible"
x:Name="BoardImageOptionsPanel"
CornerRadius="5" Background="{DynamicResource FloatBarBackground}"
Opacity="1"
BorderBrush="#2563eb" BorderThickness="1">
<ikw:SimpleStackPanel Margin="0">
<Border BorderBrush="#1e3a8a" BorderThickness="0,0,0,1"
CornerRadius="6,6,0,0"
Background="#2563eb" Margin="-1,-1,-1,1">
<Canvas Height="24" ClipToBounds="True">
<TextBlock Text="{i18n:I18n Key=Board_SelectImage}" Canvas.Left="8" Foreground="White"
Padding="0,5"
FontSize="11" FontWeight="Bold"
TextAlignment="Center" />
<Image Margin="100,4,0,0"
Source="/Resources/new-icons/close-white.png"
RenderOptions.BitmapScalingMode="HighQuality"
Height="16" Width="16"
MouseDown="Border_MouseDown"
MouseUp="CloseImageOptionsPanel_MouseUp" />
</Canvas>
</Border>
<ikw:SimpleStackPanel Margin="6,4,6,4" Spacing="2">
<!-- Screenshot Option -->
<Border MouseDown="Border_MouseDown" MouseUp="ImageOptionScreenshot_MouseUp"
Background="Transparent" CornerRadius="3" Padding="6,4">
<Border.Style>
<Style TargetType="Border">
<Setter Property="Background" Value="Transparent"/>
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Background" Value="#f0f9ff"/>
</Trigger>
</Style.Triggers>
</Style>
</Border.Style>
<ikw:SimpleStackPanel Orientation="Horizontal" Spacing="6">
<Image Height="16" Width="16">
<Image.Source>
<DrawingImage>
<DrawingImage.Drawing>
<DrawingGroup ClipGeometry="M0,0 V24 H24 V0 H0 Z">
<GeometryDrawing Brush="#2563eb"
Geometry="{Binding Source={x:Static icons:XamlGraphicsIconGeometries.ScreenshotIconGeometry}, Converter={StaticResource StringToGeometryConverter}}"/>
</DrawingGroup>
</DrawingImage.Drawing>
</DrawingImage>
</Image.Source>
</Image>
<TextBlock Text="{i18n:I18n Key=Board_Screenshot}" FontSize="10"
Foreground="{DynamicResource TextForeground}" VerticalAlignment="Center"/>
</ikw:SimpleStackPanel>
</Border>
<!-- Select Image Option -->
<Border MouseDown="Border_MouseDown" MouseUp="ImageOptionSelectFile_MouseUp"
Background="Transparent" CornerRadius="3" Padding="6,4">
<Border.Style>
<Style TargetType="Border">
<Setter Property="Background" Value="Transparent"/>
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Background" Value="#f0f9ff"/>
</Trigger>
</Style.Triggers>
</Style>
</Border.Style>
<ikw:SimpleStackPanel Orientation="Horizontal" Spacing="6">
<Image Height="16" Width="16">
<Image.Source>
<DrawingImage>
<DrawingImage.Drawing>
<DrawingGroup ClipGeometry="M0,0 V24 H24 V0 H0 Z">
<GeometryDrawing Brush="#2563eb"
Geometry="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"/>
</DrawingGroup>
</DrawingImage.Drawing>
</DrawingImage>
</Image.Source>
</Image>
<TextBlock Text="{i18n:I18n Key=Board_SelectImage}" FontSize="10"
Foreground="{DynamicResource TextForeground}" VerticalAlignment="Center"/>
</ikw:SimpleStackPanel>
</Border>
</ikw:SimpleStackPanel>
</ikw:SimpleStackPanel>
</Border>
<!-- Select Image Option -->
<Border MouseDown="Border_MouseDown" MouseUp="ImageOptionSelectFile_MouseUp"
Background="Transparent" CornerRadius="3" Padding="6,4">
<Border.Style>
<Style TargetType="Border">
<Setter Property="Background" Value="Transparent"/>
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Background" Value="#f0f9ff"/>
</Trigger>
</Style.Triggers>
</Style>
</Border.Style>
<ikw:SimpleStackPanel Orientation="Horizontal" Spacing="6">
<Image Height="16" Width="16">
<Image.Source>
<DrawingImage>
<DrawingImage.Drawing>
<DrawingGroup ClipGeometry="M0,0 V24 H24 V0 H0 Z">
<GeometryDrawing Brush="#2563eb"
Geometry="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"/>
</DrawingGroup>
</DrawingImage.Drawing>
</DrawingImage>
</Image.Source>
</Image>
<TextBlock Text="{i18n:I18n Key=Board_SelectImage}" FontSize="10"
Foreground="{DynamicResource TextForeground}" VerticalAlignment="Center"/>
</ikw:SimpleStackPanel>
</Border>
</ikw:SimpleStackPanel>
</localControls:BoardMenuFrame>
</Grid>
</Border>
<controls:BoardToolbarButton x:Name="BoardUndo"
Label="{i18n:I18n Key=Board_Undo}"
IconGeometry="F1 M24,24z M0,0z M8.71408,16.8493L0.874451,9.00964 8.71408,1.17001 8.71408,7.42358 15.7239,7.42358C16.7074,7.42358 17.6791,7.62744 18.583,8.02124 19.4866,8.41493 20.3023,8.98966 20.9857,9.70849 21.6689,10.4271 22.2069,11.276 22.5726,12.2047 22.9383,13.1333 23.1256,14.126 23.1256,15.1268 23.1256,16.1276 22.9383,17.1203 22.5726,18.0489 22.2069,18.9776 21.6689,19.8264 20.9857,20.5451 20.3023,21.2639 19.4866,21.8387 18.583,22.2324 17.6791,22.6262 16.7074,22.83 15.7239,22.83L10.437,22.83 10.437,19.6579 15.7239,19.6579C16.2679,19.6579 16.8086,19.5453 17.3159,19.3243 17.8235,19.1031 18.29,18.7767 18.6867,18.3594 19.0835,17.942 19.4023,17.4422 19.6211,16.8866 19.8399,16.3308 19.9534,15.7326 19.9534,15.1268 19.9534,14.5209 19.8399,13.9227 19.6211,13.367 19.4023,12.8114 19.0835,12.3115 18.6867,11.8941 18.29,11.4769 17.8235,11.1505 17.3159,10.9293 16.8086,10.7083 16.2679,10.5957 15.7239,10.5957L8.71408,10.5957 8.71408,16.8493z"
@@ -2002,66 +2010,67 @@
Label="{i18n:I18n Key=Board_Tools}"
IconGeometry="F1 M24,24z M0,0z M3.336,1.17001C2.13975,1.17001,1.17,2.13976,1.17,3.33601L1.17,8.75101C1.17,9.94726,2.13975,10.917,3.336,10.917L8.751,10.917C9.94725,10.917,10.917,9.94726,10.917,8.75101L10.917,3.33601C10.917,2.13976,9.94725,1.17001,8.751,1.17001L3.336,1.17001z M15.249,1.17001C14.0527,1.17001,13.083,2.13976,13.083,3.33601L13.083,8.75101C13.083,9.94726,14.0527,10.917,15.249,10.917L20.664,10.917C21.8602,10.917,22.83,9.94726,22.83,8.75101L22.83,3.33601C22.83,2.13976,21.8602,1.17001,20.664,1.17001L15.249,1.17001z M3.336,13.083C2.13975,13.083,1.17,14.0528,1.17,15.249L1.17,20.664C1.17,21.8603,2.13975,22.83,3.336,22.83L8.751,22.83C9.94725,22.83,10.917,21.8603,10.917,20.664L10.917,15.249C10.917,14.0528,9.94725,13.083,8.751,13.083L3.336,13.083z M15.249,13.083C14.0527,13.083,13.083,14.0528,13.083,15.249L13.083,20.664C13.083,21.8603,14.0527,22.83,15.249,22.83L20.664,22.83C21.8602,22.83,22.83,21.8603,22.83,20.664L22.83,15.249C22.83,14.0528,21.8602,13.083,20.664,13.083L15.249,13.083z"
ButtonMouseUp="SymbolIconTools_MouseUp" />
<Border>
<Grid Width="0" Margin="0,0,0,5" RenderTransformOrigin="0,1">
<Grid.RenderTransform>
<TransformGroup>
<ScaleTransform ScaleX="1.5" ScaleY="1.5" />
<SkewTransform />
<RotateTransform />
<TranslateTransform />
</TransformGroup>
</Grid.RenderTransform>
<Border ClipToBounds="True" x:Name="BoardBorderTools"
Visibility="Collapsed"
Margin="-80,-160,-39,50"
CornerRadius="5" Background="{DynamicResource SettingsPageBackground}" Opacity="1"
BorderThickness="1"
BorderBrush="#2563eb">
<ikw:SimpleStackPanel Margin="-1,0,0,0">
<Border BorderBrush="#1e3a8a" BorderThickness="0,0,0,1"
CornerRadius="6,6,0,0"
Background="#2563eb" Margin="-1,-1,-1,1">
<Grid Height="24">
<TextBlock Text="{i18n:I18n Key=Tools_MoreFeaturesTitle}" Margin="8,0,0,0" Foreground="White"
VerticalAlignment="Center" HorizontalAlignment="Left"
FontSize="11" FontWeight="Bold" />
<Image Margin="0,0,8,0"
Source="/Resources/new-icons/close-white.png"
RenderOptions.BitmapScalingMode="HighQuality"
Height="16" Width="16"
HorizontalAlignment="Right" VerticalAlignment="Center"
MouseDown="Border_MouseDown"
MouseUp="CloseBordertools_MouseUp" />
</Grid>
</Border>
<ikw:SimpleStackPanel Margin="10,3,10,2" Spacing="-2">
<ikw:SimpleStackPanel.Resources>
<Style TargetType="Label" BasedOn="{StaticResource AutoFitToolPopupLabel8}" />
</ikw:SimpleStackPanel.Resources>
<ikw:SimpleStackPanel Margin="0,0,0,0" Height="40"
Orientation="Horizontal">
<controls:ToolMenuButton x:Name="BoardTimerToolBtn" ButtonMouseUp="ImageCountdownTimer_MouseUp" Label="{i18n:I18n Key=Tools_Timer}" />
<controls:ToolMenuButton x:Name="BoardRandomDrawToolBtn" Visibility="Collapsed" ButtonMouseUp="SymbolIconRand_MouseUp" Label="{i18n:I18n Key=Tools_RandomDraw}" />
<controls:ToolMenuButton x:Name="BoardSingleDrawToolBtn" Visibility="Collapsed" ButtonMouseUp="SymbolIconRandOne_MouseUp" Label="{i18n:I18n Key=Tools_SingleDraw}" />
</ikw:SimpleStackPanel>
<ikw:SimpleStackPanel Margin="0,0,0,0" Height="40"
Orientation="Horizontal">
<controls:ToolMenuButton x:Name="BoardSaveToolBtn" ButtonMouseDown="Border_MouseDown" ButtonMouseUp="SymbolIconSaveStrokes_MouseUp" Label="{i18n:I18n Key=Tools_Save}" />
<controls:ToolMenuButton x:Name="BoardOpenToolBtn" ButtonMouseDown="Border_MouseDown" ButtonMouseUp="SymbolIconOpenStrokes_MouseUp" Label="{i18n:I18n Key=Tools_Open}" />
<controls:ToolMenuButton x:Name="BoardReplayToolBtn" ButtonMouseUp="GridInkReplayButton_MouseUp" Label="{i18n:I18n Key=Tools_Replay}" />
</ikw:SimpleStackPanel>
<ikw:SimpleStackPanel Margin="0,0,0,0" Height="40"
Orientation="Horizontal">
<controls:ToolMenuButton x:Name="BoardScreenshotToolBtn" ButtonMouseUp="SymbolIconScreenshot_MouseUp" Label="{i18n:I18n Key=Tools_Screenshot}" />
<controls:ToolMenuButton x:Name="BoardManualToolBtn" ButtonMouseUp="OperatingGuideWindowIcon_MouseUp" Label="{i18n:I18n Key=Tools_Manual}" />
<controls:ToolMenuButton x:Name="BoardSettingsToolBtn" ButtonMouseUp="SymbolIconSettings_Click" Label="{i18n:I18n Key=Tools_Settings}" />
</ikw:SimpleStackPanel>
</ikw:SimpleStackPanel>
</ikw:SimpleStackPanel>
<!--工具菜单-->
<Border>
<Grid Width="0" Margin="0,0,0,5" RenderTransformOrigin="0,1">
<Grid.RenderTransform>
<TransformGroup>
<ScaleTransform ScaleX="1.5" ScaleY="1.5" />
<SkewTransform />
<RotateTransform />
<TranslateTransform />
</TransformGroup>
</Grid.RenderTransform>
<Border ClipToBounds="True" x:Name="BoardBorderTools"
Visibility="Collapsed"
Margin="-80,-160,-39,50"
CornerRadius="5" Background="{DynamicResource SettingsPageBackground}" Opacity="1"
BorderThickness="1"
BorderBrush="#2563eb">
<ikw:SimpleStackPanel Margin="-1,0,0,0">
<Border BorderBrush="#1e3a8a" BorderThickness="0,0,0,1"
CornerRadius="6,6,0,0"
Background="#2563eb" Margin="-1,-1,-1,1">
<Grid Height="24">
<TextBlock Text="{i18n:I18n Key=Tools_MoreFeaturesTitle}" Margin="8,0,0,0" Foreground="White"
VerticalAlignment="Center" HorizontalAlignment="Left"
FontSize="11" FontWeight="Bold" />
<Image Margin="0,0,8,0"
Source="/Resources/new-icons/close-white.png"
RenderOptions.BitmapScalingMode="HighQuality"
Height="16" Width="16"
HorizontalAlignment="Right" VerticalAlignment="Center"
MouseDown="Border_MouseDown"
MouseUp="CloseBordertools_MouseUp" />
</Grid>
</Border>
</Grid>
<ikw:SimpleStackPanel Margin="10,3,10,2" Spacing="2">
<ikw:SimpleStackPanel.Resources>
<Style TargetType="Label" BasedOn="{StaticResource AutoFitToolPopupLabel8}" />
</ikw:SimpleStackPanel.Resources>
<ikw:SimpleStackPanel Margin="0,0,0,0" Height="40"
Orientation="Horizontal">
<controls:ToolMenuButton x:Name="BoardTimerToolBtn" ButtonMouseUp="ImageCountdownTimer_MouseUp" Label="{i18n:I18n Key=Tools_Timer}" />
<controls:ToolMenuButton x:Name="BoardRandomDrawToolBtn" Visibility="Collapsed" ButtonMouseUp="SymbolIconRand_MouseUp" Label="{i18n:I18n Key=Tools_RandomDraw}" />
<controls:ToolMenuButton x:Name="BoardSingleDrawToolBtn" Visibility="Collapsed" ButtonMouseUp="SymbolIconRandOne_MouseUp" Label="{i18n:I18n Key=Tools_SingleDraw}" />
</ikw:SimpleStackPanel>
<ikw:SimpleStackPanel Margin="0,0,0,0" Height="40"
Orientation="Horizontal">
<controls:ToolMenuButton x:Name="BoardSaveToolBtn" ButtonMouseDown="Border_MouseDown" ButtonMouseUp="SymbolIconSaveStrokes_MouseUp" Label="{i18n:I18n Key=Tools_Save}" />
<controls:ToolMenuButton x:Name="BoardOpenToolBtn" ButtonMouseDown="Border_MouseDown" ButtonMouseUp="SymbolIconOpenStrokes_MouseUp" Label="{i18n:I18n Key=Tools_Open}" />
<controls:ToolMenuButton x:Name="BoardReplayToolBtn" ButtonMouseUp="GridInkReplayButton_MouseUp" Label="{i18n:I18n Key=Tools_Replay}" />
</ikw:SimpleStackPanel>
<ikw:SimpleStackPanel Margin="0,0,0,0" Height="40"
Orientation="Horizontal">
<controls:ToolMenuButton x:Name="BoardScreenshotToolBtn" ButtonMouseUp="SymbolIconScreenshot_MouseUp" Label="{i18n:I18n Key=Tools_Screenshot}" />
<controls:ToolMenuButton x:Name="BoardManualToolBtn" ButtonMouseUp="OperatingGuideWindowIcon_MouseUp" Label="{i18n:I18n Key=Tools_Manual}" />
<controls:ToolMenuButton x:Name="BoardSettingsToolBtn" ButtonMouseUp="SymbolIconSettings_Click" Label="{i18n:I18n Key=Tools_Settings}" />
</ikw:SimpleStackPanel>
</ikw:SimpleStackPanel>
</ikw:SimpleStackPanel>
</Border>
</Grid>
</Border>
<controls:BoardToolbarButton x:Name="BoardExit"
Position="Last"
Label="{i18n:I18n Key=Board_Exit}"
@@ -2935,7 +2944,7 @@
MouseUp="CloseBordertools_MouseUp" />
</Canvas>
</Border>
<ikw:SimpleStackPanel Orientation="Horizontal" Margin="0,8,0,0" Spacing="-2"
<ikw:SimpleStackPanel Orientation="Horizontal" Margin="0,8,0,0" Spacing="2"
VerticalAlignment="Center"
HorizontalAlignment="Center">
<Label Margin="0,0,0,0" Content="{i18n:I18n Key=Board_Size}" FontWeight="Bold"
@@ -3074,7 +3083,7 @@
</ikw:SimpleStackPanel>
</Border>
<!---->
<ikw:SimpleStackPanel Margin="10,3,10,2" Spacing="-2">
<ikw:SimpleStackPanel Margin="10,3,10,2" Spacing="2">
<ikw:SimpleStackPanel.Resources>
<Style TargetType="Label" BasedOn="{StaticResource AutoFitToolPopupLabel8}" />
</ikw:SimpleStackPanel.Resources>
+1 -1
View File
@@ -589,7 +589,7 @@
<value>识别引擎</value>
</data>
<data name="InkRecog_ShapeEngineHint" xml:space="preserve">
<value> 自动:64 位进程使用 WinRTWindows 10+),32 位使用 IACore。可强制指定 IACore 或 WinRT。</value>
<value> 自动:在 Windows 10+ 使用 WinRT,否则使用 IACore。可强制指定 IACore 或 WinRT。IACore 通过 IPC 辅助进程运行,在 32/64 位主进程下均可用。</value>
</data>
<data name="InkRecog_ShapeEngineAuto" xml:space="preserve">
<value>自动</value>
-61
View File
@@ -1,61 +0,0 @@
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:controls="clr-namespace:Ink_Canvas.Controls">
<Style TargetType="{x:Type controls:BoardMenuFrame}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type controls:BoardMenuFrame}">
<Popup IsOpen="{Binding IsOpen, RelativeSource={RelativeSource TemplatedParent}}"
PlacementTarget="{TemplateBinding PlacementTarget}"
Placement="{TemplateBinding Placement}"
CustomPopupPlacementCallback="{TemplateBinding CustomPopupPlacementCallback}"
HorizontalOffset="{TemplateBinding PopupHorizontalOffset}"
VerticalOffset="{TemplateBinding PopupVerticalOffset}"
AllowsTransparency="True"
StaysOpen="True"
PopupAnimation="None">
<Border x:Name="PART_AnimationRoot"
BorderBrush="{TemplateBinding HeaderBackground}"
BorderThickness="1"
CornerRadius="{TemplateBinding PanelCornerRadius}"
Background="{TemplateBinding PanelBackground}">
<DockPanel LastChildFill="True">
<Border DockPanel.Dock="Top"
Background="{TemplateBinding HeaderBackground}"
BorderBrush="{TemplateBinding HeaderBorderBrush}"
BorderThickness="0,0,0,1"
CornerRadius="{TemplateBinding HeaderCornerRadius}"
Height="{TemplateBinding HeaderHeight}"
Margin="-1,-1,-1,0">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<ContentPresenter Grid.Column="0"
Content="{TemplateBinding Title}"
VerticalAlignment="Center"
HorizontalAlignment="Left"
Margin="8,0,0,0"
TextElement.FontSize="{TemplateBinding TitleFontSize}"
TextElement.FontWeight="Bold"
TextElement.Foreground="White" />
<Image Grid.Column="2"
x:Name="PART_CloseImage"
Source="/Resources/new-icons/close-white.png"
Width="16" Height="16"
RenderOptions.BitmapScalingMode="HighQuality"
VerticalAlignment="Center"
Margin="0,0,8,0" />
</Grid>
</Border>
<ContentPresenter Margin="12" />
</DockPanel>
</Border>
</Popup>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ResourceDictionary>
@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<startup useLegacyV2RuntimeActivationPolicy="true">
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.7.2" />
</startup>
</configuration>
@@ -0,0 +1,56 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">x86</Platform>
<ProjectGuid>{B1A2C3D4-E5F6-7890-ABCD-EF1234567891}</ProjectGuid>
<OutputType>Exe</OutputType>
<RootNamespace>InkCanvasForClass.IACoreHelper</RootNamespace>
<AssemblyName>InkCanvasForClass.IACoreHelper</AssemblyName>
<TargetFrameworkVersion>v4.7.2</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
<Deterministic>true</Deterministic>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|x86' ">
<PlatformTarget>x86</PlatformTarget>
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|x86' ">
<PlatformTarget>x86</PlatformTarget>
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<ItemGroup>
<Reference Include="System" />
<Reference Include="System.Core" />
<Reference Include="PresentationCore" />
<Reference Include="PresentationFramework" />
<Reference Include="WindowsBase" />
<Reference Include="System.Xml" />
<Reference Include="IAWinFX">
<HintPath>..\Ink Canvas\Resources\IACore\IAWinFX.dll</HintPath>
<Private>false</Private>
</Reference>
</ItemGroup>
<ItemGroup>
<Compile Include="IpcProtocol.cs" />
<Compile Include="Program.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
</ItemGroup>
<ItemGroup>
<None Include="App.config" />
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
</Project>
@@ -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;
}
}
}
+200
View File
@@ -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 <parentPid>");
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");
}
}
}
@@ -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")]