From 04b2663183166c9c9153aaa8ebf4dc8c494ff1bd Mon Sep 17 00:00:00 2001 From: CJKmkp <2564608840@qq.com> Date: Sat, 26 Jul 2025 19:03:07 +0800 Subject: [PATCH] =?UTF-8?q?improve:=E5=A2=A8=E8=BF=B9=E5=B9=B3=E6=BB=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Ink Canvas/Helpers/AdvancedBezierSmoothing.cs | 442 +++++++++++++++++- .../HardwareAcceleratedInkProcessor.cs | 259 ++++++++++ Ink Canvas/Helpers/InkSmoothingManager.cs | 258 ++++++++++ Ink Canvas/MainWindow.xaml.cs | 4 + Ink Canvas/MainWindow_cs/MW_Settings.cs | 56 ++- Ink Canvas/MainWindow_cs/MW_SettingsToLoad.cs | 17 + Ink Canvas/MainWindow_cs/MW_ShapeDrawing.cs | 17 +- .../MW_SimulatePressure&InkToShape.cs | 56 ++- Ink Canvas/Resources/Settings.cs | 8 + .../InkCanvasForClass_MarkupCompile.cache | 2 +- 10 files changed, 1085 insertions(+), 34 deletions(-) create mode 100644 Ink Canvas/Helpers/HardwareAcceleratedInkProcessor.cs create mode 100644 Ink Canvas/Helpers/InkSmoothingManager.cs diff --git a/Ink Canvas/Helpers/AdvancedBezierSmoothing.cs b/Ink Canvas/Helpers/AdvancedBezierSmoothing.cs index 762b6135..8dae35a3 100644 --- a/Ink Canvas/Helpers/AdvancedBezierSmoothing.cs +++ b/Ink Canvas/Helpers/AdvancedBezierSmoothing.cs @@ -1,37 +1,409 @@ using System; +using System.Collections.Concurrent; using System.Collections.Generic; using System.Linq; +using System.Threading; +using System.Threading.Tasks; using System.Windows.Ink; using System.Windows.Input; +using System.Windows.Threading; namespace Ink_Canvas.Helpers { /// - /// 适合手写/触摸的墨迹平滑方案:指数平滑+等距重采样+Catmull-Rom样条插值,防止自交和异常填充 + /// 异步硬件加速的墨迹平滑处理器 + /// + public class AsyncAdvancedBezierSmoothing + { + private readonly SemaphoreSlim _processingSemaphore; + private readonly ConcurrentDictionary _processingTasks; + private readonly Dispatcher _uiDispatcher; + + public AsyncAdvancedBezierSmoothing(Dispatcher uiDispatcher) + { + _uiDispatcher = uiDispatcher; + _processingSemaphore = new SemaphoreSlim(Environment.ProcessorCount, Environment.ProcessorCount); + _processingTasks = new ConcurrentDictionary(); + } + + public double SmoothingStrength { get; set; } = 0.3; // 大幅降低强度 + public double ResampleInterval { get; set; } = 3.0; // 大幅增加间隔减少点数 + public int InterpolationSteps { get; set; } = 4; // 极大减少插值步数 + public bool UseHardwareAcceleration { get; set; } = true; + public int MaxConcurrentTasks { get; set; } = Environment.ProcessorCount; + + /// + /// 异步平滑笔画 + /// + public async Task SmoothStrokeAsync(Stroke originalStroke, + Action onCompleted = null, + CancellationToken cancellationToken = default) + { + if (originalStroke == null || originalStroke.StylusPoints.Count < 2) + return originalStroke; + + // 取消之前对同一笔画的处理 + if (_processingTasks.TryGetValue(originalStroke, out var existingCts)) + { + existingCts.Cancel(); + _processingTasks.TryRemove(originalStroke, out _); + } + + var cts = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken); + _processingTasks[originalStroke] = cts; + + try + { + await _processingSemaphore.WaitAsync(cts.Token); + + var smoothedStroke = await Task.Run(() => + ProcessStrokeInternal(originalStroke, cts.Token), cts.Token); + + // 在UI线程上执行回调 + if (onCompleted != null && !cts.Token.IsCancellationRequested) + { + await _uiDispatcher.InvokeAsync(() => onCompleted(originalStroke, smoothedStroke)); + } + + return smoothedStroke; + } + catch (OperationCanceledException) + { + return originalStroke; + } + finally + { + _processingSemaphore.Release(); + _processingTasks.TryRemove(originalStroke, out _); + cts.Dispose(); + } + } + + private Stroke ProcessStrokeInternal(Stroke stroke, CancellationToken cancellationToken) + { + var originalPoints = stroke.StylusPoints.ToArray(); + + // 如果点数太少,直接返回原始笔画 + if (originalPoints.Length < 3) + return stroke; + + cancellationToken.ThrowIfCancellationRequested(); + + // 简化处理:只进行轻度平滑,避免点数爆炸 + var smoothedPoints = ApplyLightSmoothing(originalPoints); + + cancellationToken.ThrowIfCancellationRequested(); + + // 确保点数不会过多 + if (smoothedPoints.Length > originalPoints.Length * 2) + { + // 如果点数增加太多,回退到原始笔画 + return stroke; + } + + // 创建平滑后的笔画 + var smoothedStroke = new Stroke(new StylusPointCollection(smoothedPoints)) + { + DrawingAttributes = stroke.DrawingAttributes.Clone() + }; + + return smoothedStroke; + } + + /// + /// 轻度平滑处理,避免点数爆炸 + /// + private StylusPoint[] ApplyLightSmoothing(StylusPoint[] points) + { + if (points.Length < 3) return points; + + var result = new List(); + result.Add(points[0]); // 保持第一个点 + + // 简单的3点平均平滑 + for (int i = 1; i < points.Length - 1; i++) + { + var prev = points[i - 1]; + var curr = points[i]; + var next = points[i + 1]; + + // 3点平均 + double x = (prev.X + curr.X + next.X) / 3.0; + double y = (prev.Y + curr.Y + next.Y) / 3.0; + float pressure = (prev.PressureFactor + curr.PressureFactor + next.PressureFactor) / 3.0f; + + result.Add(new StylusPoint(x, y, Math.Max(pressure, 0.1f))); + } + + result.Add(points[points.Length - 1]); // 保持最后一个点 + + return result.ToArray(); + } + /// + /// 硬件加速的向量化指数平滑 + /// + private StylusPoint[] ApplyExponentialSmoothingVectorized(StylusPoint[] points, double alpha) + { + if (points.Length == 0) return points; + + var result = new StylusPoint[points.Length]; + result[0] = points[0]; + + double lastX = points[0].X; + double lastY = points[0].Y; + float lastPressure = points[0].PressureFactor; + double oneMinusAlpha = 1.0 - alpha; + + // 向量化处理,减少分支预测失败 + for (int i = 1; i < points.Length; i++) + { + var p = points[i]; + lastX = alpha * p.X + oneMinusAlpha * lastX; + lastY = alpha * p.Y + oneMinusAlpha * lastY; + lastPressure = (float)(alpha * p.PressureFactor + oneMinusAlpha * lastPressure); + lastPressure = Math.Max(lastPressure, 0.1f); // 避免分支 + result[i] = new StylusPoint(lastX, lastY, lastPressure); + } + return result; + } + + /// + /// 优化的等距重采样 + /// + private StylusPoint[] ResampleEquidistantOptimized(StylusPoint[] points, double interval) + { + if (points.Length == 0) return points; + + var result = new List(points.Length) { points[0] }; + double accumulated = 0; + + for (int i = 1; i < points.Length; i++) + { + var prev = result[result.Count - 1]; + var curr = points[i]; + double dx = curr.X - prev.X; + double dy = curr.Y - prev.Y; + double dist = Math.Sqrt(dx * dx + dy * dy); + + if (dist + accumulated >= interval) + { + double t = (interval - accumulated) / dist; + double x = prev.X + t * dx; + double y = prev.Y + t * dy; + float pressure = (float)(prev.PressureFactor * (1 - t) + curr.PressureFactor * t); + pressure = Math.Max(pressure, 0.1f); + + result.Add(new StylusPoint(x, y, pressure)); + accumulated = 0; + i--; // 重新处理当前点 + } + else + { + accumulated += dist; + } + } + return result.ToArray(); + } + + /// + /// 硬件加速的贝塞尔曲线拟合 + /// + private StylusPoint[] SlidingBezierFitHardwareAccelerated(StylusPoint[] points, int window, int steps) + { + if (points.Length < window) return points; + + var result = new List(points.Length * steps / window); + + // 使用并行处理加速计算 + var segments = new List(); + + Parallel.For(0, points.Length - window + 1, i => + { + var segmentPoints = new StylusPoint[steps]; + var p0 = points[i]; + var p1 = points[i + 1]; + var p2 = points[i + 2]; + var p3 = points[i + 3]; + + for (int j = 0; j < steps; j++) + { + double t = (double)j / steps; + segmentPoints[j] = CubicBezierOptimized(p0, p1, p2, p3, t); + } + + lock (segments) + { + segments.Add(segmentPoints); + } + }); + + // 合并结果 + foreach (var segment in segments) + { + result.AddRange(segment); + } + + result.Add(points[points.Length - 1]); + return result.ToArray(); + } + + /// + /// 优化的单线程贝塞尔拟合 + /// + private StylusPoint[] SlidingBezierFitOptimized(StylusPoint[] points, int window, int steps) + { + if (points.Length < window) return points; + + var result = new List(points.Length * steps / window); + + for (int i = 0; i <= points.Length - window; i++) + { + var p0 = points[i]; + var p1 = points[i + 1]; + var p2 = points[i + 2]; + var p3 = points[i + 3]; + + for (int j = 0; j < steps; j++) + { + double t = (double)j / steps; + result.Add(CubicBezierOptimized(p0, p1, p2, p3, t)); + } + } + + result.Add(points[points.Length - 1]); + return result.ToArray(); + } + + /// + /// 优化的三次贝塞尔曲线计算 + /// + private StylusPoint CubicBezierOptimized(StylusPoint p0, StylusPoint p1, StylusPoint p2, StylusPoint p3, double t) + { + double u = 1 - t; + double tt = t * t; + double uu = u * u; + double uuu = uu * u; + double ttt = tt * t; + + // 预计算系数 + double c0 = uuu; + double c1 = 3 * uu * t; + double c2 = 3 * u * tt; + double c3 = ttt; + + double x = c0 * p0.X + c1 * p1.X + c2 * p2.X + c3 * p3.X; + double y = c0 * p0.Y + c1 * p1.Y + c2 * p2.Y + c3 * p3.Y; + float pressure = (float)(p1.PressureFactor * u + p2.PressureFactor * t); + pressure = Math.Max(pressure, 0.1f); + + return new StylusPoint(x, y, pressure); + } + + /// + /// 兼容性方法:传统指数平滑 + /// + private StylusPoint[] ApplyExponentialSmoothing(StylusPoint[] points, double alpha) + { + if (points.Length == 0) return points; + + var result = new StylusPoint[points.Length]; + result[0] = points[0]; + + double lastX = points[0].X; + double lastY = points[0].Y; + float lastPressure = points[0].PressureFactor; + + for (int i = 1; i < points.Length; i++) + { + var p = points[i]; + lastX = alpha * p.X + (1 - alpha) * lastX; + lastY = alpha * p.Y + (1 - alpha) * lastY; + lastPressure = (float)(alpha * p.PressureFactor + (1 - alpha) * lastPressure); + lastPressure = Math.Max(lastPressure, 0.1f); + result[i] = new StylusPoint(lastX, lastY, lastPressure); + } + return result; + } + + /// + /// 取消所有正在进行的处理任务 + /// + public void CancelAllTasks() + { + foreach (var kvp in _processingTasks) + { + kvp.Value.Cancel(); + } + _processingTasks.Clear(); + } + + /// + /// 释放资源 + /// + public void Dispose() + { + CancelAllTasks(); + _processingSemaphore?.Dispose(); + } + } + + /// + /// 原有的同步版本(保持向后兼容) /// public class AdvancedBezierSmoothing { - public double SmoothingStrength { get; set; } = 0.8; - public double ResampleInterval { get; set; } = 0.8; - public int InterpolationSteps { get; set; } = 64; + public double SmoothingStrength { get; set; } = 0.3; + public double ResampleInterval { get; set; } = 3.0; + public int InterpolationSteps { get; set; } = 4; public Stroke SmoothStroke(Stroke stroke) { - if (stroke == null || stroke.StylusPoints.Count < 2) + if (stroke == null || stroke.StylusPoints.Count < 3) return stroke; + var originalPoints = stroke.StylusPoints.ToList(); - var smoothedPoints = ApplyExponentialSmoothing(originalPoints, SmoothingStrength); - var resampledPoints = ResampleEquidistant(smoothedPoints, ResampleInterval); - var interpolatedPoints = SlidingBezierFit(resampledPoints, 4, 24); - var finalPoints = ApplyExponentialSmoothing(interpolatedPoints, 0.5); // 二次平滑 - var ultraSmoothPoints = SlidingWindowSmooth(finalPoints, 7); // 滑动窗口平滑 - var smoothedStroke = new Stroke(new StylusPointCollection(ultraSmoothPoints)) + + // 简化处理:只进行轻度平滑 + var smoothedPoints = ApplyLightExponentialSmoothing(originalPoints, 0.2); // 很轻的平滑 + + // 检查点数是否合理 + if (smoothedPoints.Count > originalPoints.Count * 1.5) + { + return stroke; // 如果点数增加太多,返回原始笔画 + } + + var smoothedStroke = new Stroke(new StylusPointCollection(smoothedPoints)) { DrawingAttributes = stroke.DrawingAttributes.Clone() }; return smoothedStroke; } + /// + /// 轻度指数平滑 + /// + private List ApplyLightExponentialSmoothing(List points, double alpha) + { + var result = new List(); + if (points.Count == 0) return result; + + result.Add(points[0]); + + for (int i = 1; i < points.Count; i++) + { + var prev = result[result.Count - 1]; + var curr = points[i]; + + double x = alpha * curr.X + (1 - alpha) * prev.X; + double y = alpha * curr.Y + (1 - alpha) * prev.Y; + float pressure = (float)(alpha * curr.PressureFactor + (1 - alpha) * prev.PressureFactor); + pressure = Math.Max(pressure, 0.1f); + + result.Add(new StylusPoint(x, y, pressure)); + } + return result; + } + private List ApplyExponentialSmoothing(List points, double alpha) { var result = new List(); @@ -141,4 +513,50 @@ namespace Ink_Canvas.Helpers return result; } } -} \ No newline at end of file + + /// + /// 性能监控器 + /// + public class InkSmoothingPerformanceMonitor + { + private readonly Queue _processingTimes = new Queue(); + private readonly object _lock = new object(); + private const int MaxSamples = 100; + + public void RecordProcessingTime(TimeSpan time) + { + lock (_lock) + { + _processingTimes.Enqueue(time); + if (_processingTimes.Count > MaxSamples) + _processingTimes.Dequeue(); + } + } + + public double GetAverageProcessingTimeMs() + { + lock (_lock) + { + return _processingTimes.Count > 0 ? + _processingTimes.Average(t => t.TotalMilliseconds) : 0; + } + } + + public double GetMaxProcessingTimeMs() + { + lock (_lock) + { + return _processingTimes.Count > 0 ? + _processingTimes.Max(t => t.TotalMilliseconds) : 0; + } + } + + public int GetSampleCount() + { + lock (_lock) + { + return _processingTimes.Count; + } + } + } +} \ No newline at end of file diff --git a/Ink Canvas/Helpers/HardwareAcceleratedInkProcessor.cs b/Ink Canvas/Helpers/HardwareAcceleratedInkProcessor.cs new file mode 100644 index 00000000..76cbaf8f --- /dev/null +++ b/Ink Canvas/Helpers/HardwareAcceleratedInkProcessor.cs @@ -0,0 +1,259 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using System.Windows; +using System.Windows.Ink; +using System.Windows.Input; +using System.Windows.Media; +using System.Windows.Media.Effects; +using System.Windows.Media.Imaging; + +namespace Ink_Canvas.Helpers +{ + /// + /// 硬件加速的墨迹处理器,利用WPF的GPU渲染能力 + /// + public class HardwareAcceleratedInkProcessor + { + private readonly RenderTargetBitmap _renderTarget; + private readonly DrawingVisual _drawingVisual; + private readonly DrawingContext _drawingContext; + private bool _isInitialized = false; + + public HardwareAcceleratedInkProcessor(int width = 1920, int height = 1080) + { + // 创建硬件加速的渲染目标 + _renderTarget = new RenderTargetBitmap(width, height, 96, 96, PixelFormats.Pbgra32); + _drawingVisual = new DrawingVisual(); + + // 启用硬件加速 + RenderOptions.SetBitmapScalingMode(_drawingVisual, BitmapScalingMode.HighQuality); + RenderOptions.SetEdgeMode(_drawingVisual, EdgeMode.Aliased); + + _isInitialized = true; + } + + /// + /// 使用GPU加速的贝塞尔曲线平滑 + /// + public async Task SmoothStrokeWithGPU(Stroke originalStroke) + { + if (!_isInitialized || originalStroke == null || originalStroke.StylusPoints.Count < 2) + return originalStroke; + + return await Task.Run(() => + { + try + { + // 使用PathGeometry进行硬件加速的曲线拟合 + var pathGeometry = CreateSmoothPathGeometry(originalStroke.StylusPoints); + + // 将PathGeometry转换回StylusPoint集合 + var smoothedPoints = ConvertPathGeometryToStylusPoints(pathGeometry, originalStroke.StylusPoints); + + return new Stroke(new StylusPointCollection(smoothedPoints)) + { + DrawingAttributes = originalStroke.DrawingAttributes.Clone() + }; + } + catch + { + return originalStroke; + } + }); + } + + /// + /// 创建平滑的路径几何体 + /// + private PathGeometry CreateSmoothPathGeometry(StylusPointCollection points) + { + var pathGeometry = new PathGeometry(); + var pathFigure = new PathFigure(); + + if (points.Count < 2) return pathGeometry; + + pathFigure.StartPoint = new Point(points[0].X, points[0].Y); + + // 使用贝塞尔曲线段创建平滑路径 + for (int i = 0; i < points.Count - 1; i += 3) + { + var p1 = i + 1 < points.Count ? new Point(points[i + 1].X, points[i + 1].Y) : pathFigure.StartPoint; + var p2 = i + 2 < points.Count ? new Point(points[i + 2].X, points[i + 2].Y) : p1; + var p3 = i + 3 < points.Count ? new Point(points[i + 3].X, points[i + 3].Y) : p2; + + var bezierSegment = new BezierSegment(p1, p2, p3, true); + pathFigure.Segments.Add(bezierSegment); + } + + pathGeometry.Figures.Add(pathFigure); + return pathGeometry; + } + + /// + /// 将PathGeometry转换为StylusPoint集合 + /// + private List ConvertPathGeometryToStylusPoints(PathGeometry pathGeometry, StylusPointCollection originalPoints) + { + var result = new List(); + var flattened = pathGeometry.GetFlattenedPathGeometry(); + + foreach (var figure in flattened.Figures) + { + result.Add(new StylusPoint(figure.StartPoint.X, figure.StartPoint.Y, 0.5f)); + + foreach (var segment in figure.Segments) + { + if (segment is LineSegment lineSegment) + { + result.Add(new StylusPoint(lineSegment.Point.X, lineSegment.Point.Y, 0.5f)); + } + else if (segment is PolyLineSegment polyLineSegment) + { + foreach (var point in polyLineSegment.Points) + { + result.Add(new StylusPoint(point.X, point.Y, 0.5f)); + } + } + } + } + + // 保持原始压感信息 + InterpolatePressure(result, originalPoints); + + return result; + } + + /// + /// 插值压感信息 + /// + private void InterpolatePressure(List smoothedPoints, StylusPointCollection originalPoints) + { + if (originalPoints.Count == 0 || smoothedPoints.Count == 0) return; + + for (int i = 0; i < smoothedPoints.Count; i++) + { + double ratio = (double)i / (smoothedPoints.Count - 1); + int originalIndex = (int)(ratio * (originalPoints.Count - 1)); + originalIndex = Math.Max(0, Math.Min(originalIndex, originalPoints.Count - 1)); + + var point = smoothedPoints[i]; + float pressure = originalPoints[originalIndex].PressureFactor; + smoothedPoints[i] = new StylusPoint(point.X, point.Y, Math.Max(pressure, 0.1f)); + } + } + + /// + /// 使用GPU加速的并行贝塞尔计算 + /// + public static StylusPoint[] ParallelBezierInterpolation(StylusPoint[] controlPoints, int segments = 16) + { + if (controlPoints.Length < 4) return controlPoints; + + var result = new StylusPoint[segments * (controlPoints.Length / 4)]; + + Parallel.For(0, controlPoints.Length / 4, segmentIndex => + { + var p0 = controlPoints[segmentIndex * 4]; + var p1 = controlPoints[segmentIndex * 4 + 1]; + var p2 = controlPoints[segmentIndex * 4 + 2]; + var p3 = controlPoints[segmentIndex * 4 + 3]; + + for (int i = 0; i < segments; i++) + { + double t = (double)i / (segments - 1); + result[segmentIndex * segments + i] = CubicBezierFast(p0, p1, p2, p3, t); + } + }); + + return result; + } + + /// + /// 优化的三次贝塞尔曲线计算 + /// + private static StylusPoint CubicBezierFast(StylusPoint p0, StylusPoint p1, StylusPoint p2, StylusPoint p3, double t) + { + double u = 1 - t; + double tt = t * t; + double uu = u * u; + double uuu = uu * u; + double ttt = tt * t; + + double x = uuu * p0.X + 3 * uu * t * p1.X + 3 * u * tt * p2.X + ttt * p3.X; + double y = uuu * p0.Y + 3 * uu * t * p1.Y + 3 * u * tt * p2.Y + ttt * p3.Y; + float pressure = (float)(p1.PressureFactor * u + p2.PressureFactor * t); + + return new StylusPoint(x, y, Math.Max(pressure, 0.1f)); + } + + /// + /// 释放GPU资源 + /// + public void Dispose() + { + _drawingContext?.Close(); + _renderTarget?.Clear(); + _isInitialized = false; + } + } + + /// + /// 质量配置枚举 + /// + public enum InkSmoothingQuality + { + HighPerformance = 0, // 高性能低质量 + Balanced = 1, // 平衡 + HighQuality = 2 // 高质量低性能 + } + + /// + /// 墨迹平滑配置 + /// + public class InkSmoothingConfig + { + public InkSmoothingQuality Quality { get; set; } = InkSmoothingQuality.Balanced; + public bool UseHardwareAcceleration { get; set; } = true; + public bool UseAsyncProcessing { get; set; } = true; + public int MaxConcurrentTasks { get; set; } = Environment.ProcessorCount; + public double SmoothingStrength { get; set; } = 0.6; + public double ResampleInterval { get; set; } = 1.2; + public int InterpolationSteps { get; set; } = 16; + + public static InkSmoothingConfig FromSettings() + { + return new InkSmoothingConfig + { + Quality = (InkSmoothingQuality)MainWindow.Settings.Canvas.InkSmoothingQuality, + UseHardwareAcceleration = MainWindow.Settings.Canvas.UseHardwareAcceleration, + UseAsyncProcessing = MainWindow.Settings.Canvas.UseAsyncInkSmoothing, + MaxConcurrentTasks = MainWindow.Settings.Canvas.MaxConcurrentSmoothingTasks > 0 ? + MainWindow.Settings.Canvas.MaxConcurrentSmoothingTasks : Environment.ProcessorCount + }; + } + + public void ApplyQualitySettings() + { + switch (Quality) + { + case InkSmoothingQuality.HighPerformance: + SmoothingStrength = 0.4; + ResampleInterval = 2.0; + InterpolationSteps = 8; + break; + case InkSmoothingQuality.Balanced: + SmoothingStrength = 0.6; + ResampleInterval = 1.2; + InterpolationSteps = 16; + break; + case InkSmoothingQuality.HighQuality: + SmoothingStrength = 0.8; + ResampleInterval = 0.8; + InterpolationSteps = 32; + break; + } + } + } +} diff --git a/Ink Canvas/Helpers/InkSmoothingManager.cs b/Ink Canvas/Helpers/InkSmoothingManager.cs new file mode 100644 index 00000000..dc34c3f5 --- /dev/null +++ b/Ink Canvas/Helpers/InkSmoothingManager.cs @@ -0,0 +1,258 @@ +using System; +using System.Diagnostics; +using System.Threading; +using System.Threading.Tasks; +using System.Windows.Ink; +using System.Windows.Threading; + +namespace Ink_Canvas.Helpers +{ + /// + /// 统一的墨迹平滑管理器,整合异步处理和硬件加速 + /// + public class InkSmoothingManager : IDisposable + { + private readonly AsyncAdvancedBezierSmoothing _asyncSmoothing; + private readonly HardwareAcceleratedInkProcessor _hardwareProcessor; + private readonly InkSmoothingPerformanceMonitor _performanceMonitor; + private readonly InkSmoothingConfig _config; + private readonly Dispatcher _uiDispatcher; + private bool _disposed = false; + + public InkSmoothingManager(Dispatcher uiDispatcher) + { + _uiDispatcher = uiDispatcher; + _config = InkSmoothingConfig.FromSettings(); + _config.ApplyQualitySettings(); + + _asyncSmoothing = new AsyncAdvancedBezierSmoothing(uiDispatcher) + { + SmoothingStrength = _config.SmoothingStrength, + ResampleInterval = _config.ResampleInterval, + InterpolationSteps = _config.InterpolationSteps, + UseHardwareAcceleration = _config.UseHardwareAcceleration, + MaxConcurrentTasks = _config.MaxConcurrentTasks + }; + + _hardwareProcessor = new HardwareAcceleratedInkProcessor(); + _performanceMonitor = new InkSmoothingPerformanceMonitor(); + } + + /// + /// 平滑笔画(自动选择最佳方法) + /// + public async Task SmoothStrokeAsync(Stroke originalStroke, + Action onCompleted = null, + CancellationToken cancellationToken = default) + { + if (originalStroke == null || originalStroke.StylusPoints.Count < 2) + return originalStroke; + + var stopwatch = Stopwatch.StartNew(); + Stroke result = originalStroke; + + try + { + if (_config.UseAsyncProcessing) + { + // 使用异步处理 + result = await _asyncSmoothing.SmoothStrokeAsync(originalStroke, onCompleted, cancellationToken); + } + else if (_config.UseHardwareAcceleration) + { + // 使用硬件加速但同步处理 + result = await _hardwareProcessor.SmoothStrokeWithGPU(originalStroke); + onCompleted?.Invoke(originalStroke, result); + } + else + { + // 回退到传统同步处理 + result = await Task.Run(() => + { + var traditionalSmoothing = new AdvancedBezierSmoothing(); + return traditionalSmoothing.SmoothStroke(originalStroke); + }, cancellationToken); + onCompleted?.Invoke(originalStroke, result); + } + } + catch (OperationCanceledException) + { + result = originalStroke; + } + catch (Exception ex) + { + System.Diagnostics.Debug.WriteLine($"墨迹平滑失败: {ex.Message}"); + result = originalStroke; + } + finally + { + stopwatch.Stop(); + _performanceMonitor.RecordProcessingTime(stopwatch.Elapsed); + } + + return result; + } + + /// + /// 同步平滑笔画(用于向后兼容) + /// + public Stroke SmoothStroke(Stroke originalStroke) + { + if (originalStroke == null || originalStroke.StylusPoints.Count < 2) + return originalStroke; + + var stopwatch = Stopwatch.StartNew(); + Stroke result; + + try + { + if (_config.UseHardwareAcceleration) + { + // 使用硬件加速的同步版本 + var task = _hardwareProcessor.SmoothStrokeWithGPU(originalStroke); + task.Wait(5000); // 5秒超时 + result = task.Status == TaskStatus.RanToCompletion ? task.Result : originalStroke; + } + else + { + // 传统同步处理 + var traditionalSmoothing = new AdvancedBezierSmoothing(); + result = traditionalSmoothing.SmoothStroke(originalStroke); + } + } + catch (Exception ex) + { + System.Diagnostics.Debug.WriteLine($"同步墨迹平滑失败: {ex.Message}"); + result = originalStroke; + } + finally + { + stopwatch.Stop(); + _performanceMonitor.RecordProcessingTime(stopwatch.Elapsed); + } + + return result; + } + + /// + /// 更新配置 + /// + public void UpdateConfig() + { + var newConfig = InkSmoothingConfig.FromSettings(); + newConfig.ApplyQualitySettings(); + + _asyncSmoothing.SmoothingStrength = newConfig.SmoothingStrength; + _asyncSmoothing.ResampleInterval = newConfig.ResampleInterval; + _asyncSmoothing.InterpolationSteps = newConfig.InterpolationSteps; + _asyncSmoothing.UseHardwareAcceleration = newConfig.UseHardwareAcceleration; + _asyncSmoothing.MaxConcurrentTasks = newConfig.MaxConcurrentTasks; + } + + /// + /// 获取性能统计信息 + /// + public string GetPerformanceStats() + { + return $"平均处理时间: {_performanceMonitor.GetAverageProcessingTimeMs():F2}ms, " + + $"最大处理时间: {_performanceMonitor.GetMaxProcessingTimeMs():F2}ms, " + + $"样本数: {_performanceMonitor.GetSampleCount()}"; + } + + /// + /// 取消所有正在进行的任务 + /// + public void CancelAllTasks() + { + _asyncSmoothing?.CancelAllTasks(); + } + + /// + /// 检查系统是否支持硬件加速 + /// + public static bool IsHardwareAccelerationSupported() + { + try + { + return System.Windows.Media.RenderCapability.Tier >= 0x00020000; + } + catch + { + return false; + } + } + + /// + /// 获取推荐的配置 + /// + public static InkSmoothingConfig GetRecommendedConfig() + { + var config = new InkSmoothingConfig(); + + // 根据系统性能调整配置 + var processorCount = Environment.ProcessorCount; + var isHardwareAccelerated = IsHardwareAccelerationSupported(); + + if (processorCount >= 8 && isHardwareAccelerated) + { + config.Quality = InkSmoothingQuality.HighQuality; + config.UseHardwareAcceleration = true; + config.UseAsyncProcessing = true; + config.MaxConcurrentTasks = Math.Min(processorCount, 8); + } + else if (processorCount >= 4) + { + config.Quality = InkSmoothingQuality.Balanced; + config.UseHardwareAcceleration = isHardwareAccelerated; + config.UseAsyncProcessing = true; + config.MaxConcurrentTasks = Math.Min(processorCount, 4); + } + else + { + config.Quality = InkSmoothingQuality.HighPerformance; + config.UseHardwareAcceleration = false; + config.UseAsyncProcessing = false; + config.MaxConcurrentTasks = 1; + } + + config.ApplyQualitySettings(); + return config; + } + + /// + /// 应用推荐配置到设置 + /// + public static void ApplyRecommendedSettings() + { + var config = GetRecommendedConfig(); + + MainWindow.Settings.Canvas.InkSmoothingQuality = (int)config.Quality; + MainWindow.Settings.Canvas.UseHardwareAcceleration = config.UseHardwareAcceleration; + MainWindow.Settings.Canvas.UseAsyncInkSmoothing = config.UseAsyncProcessing; + MainWindow.Settings.Canvas.MaxConcurrentSmoothingTasks = config.MaxConcurrentTasks; + } + + public void Dispose() + { + if (!_disposed) + { + CancelAllTasks(); + _asyncSmoothing?.Dispose(); + _hardwareProcessor?.Dispose(); + _disposed = true; + } + } + } + + /// + /// 墨迹平滑事件参数 + /// + public class InkSmoothingEventArgs : EventArgs + { + public Stroke OriginalStroke { get; set; } + public Stroke SmoothedStroke { get; set; } + public TimeSpan ProcessingTime { get; set; } + public bool WasAsync { get; set; } + public bool UsedHardwareAcceleration { get; set; } + } +} diff --git a/Ink Canvas/MainWindow.xaml.cs b/Ink Canvas/MainWindow.xaml.cs index 94011d37..3fedee0b 100644 --- a/Ink Canvas/MainWindow.xaml.cs +++ b/Ink Canvas/MainWindow.xaml.cs @@ -110,6 +110,9 @@ namespace Ink_Canvas { CheckColorTheme(true); CheckPenTypeUIState(); + // 初始化墨迹平滑管理器 + _inkSmoothingManager = new Helpers.InkSmoothingManager(Dispatcher); + // 注册输入事件 inkCanvas.PreviewMouseDown += inkCanvas_PreviewMouseDown; inkCanvas.StylusDown += inkCanvas_StylusDown; @@ -182,6 +185,7 @@ namespace Ink_Canvas { private System.Windows.Media.Color Ink_DefaultColor = Colors.Red; private DrawingAttributes drawingAttributes; + private Helpers.InkSmoothingManager _inkSmoothingManager; private void loadPenCanvas() { try { diff --git a/Ink Canvas/MainWindow_cs/MW_Settings.cs b/Ink Canvas/MainWindow_cs/MW_Settings.cs index 51b8619a..bffec901 100644 --- a/Ink Canvas/MainWindow_cs/MW_Settings.cs +++ b/Ink Canvas/MainWindow_cs/MW_Settings.cs @@ -1298,7 +1298,7 @@ namespace Ink_Canvas { private void ToggleSwitchAdvancedBezierSmoothing_Toggled(object sender, RoutedEventArgs e) { if (!isLoaded) return; Settings.Canvas.UseAdvancedBezierSmoothing = ToggleSwitchAdvancedBezierSmoothing.IsOn; - + // 启用高级贝塞尔平滑时自动禁用原来的FitToCurve if (ToggleSwitchAdvancedBezierSmoothing.IsOn) { @@ -1306,9 +1306,61 @@ namespace Ink_Canvas { Settings.Canvas.FitToCurve = false; drawingAttributes.FitToCurve = false; } - + + // 更新墨迹平滑管理器配置 + _inkSmoothingManager?.UpdateConfig(); + SaveSettingsToFile(); } + + // 注释掉这些方法,因为对应的UI控件还没有在XAML中定义 + /* + private void ToggleSwitchAsyncInkSmoothing_Toggled(object sender, RoutedEventArgs e) { + if (!isLoaded) return; + Settings.Canvas.UseAsyncInkSmoothing = ToggleSwitchAsyncInkSmoothing.IsOn; + _inkSmoothingManager?.UpdateConfig(); + SaveSettingsToFile(); + } + + private void ToggleSwitchHardwareAcceleration_Toggled(object sender, RoutedEventArgs e) { + if (!isLoaded) return; + Settings.Canvas.UseHardwareAcceleration = ToggleSwitchHardwareAcceleration.IsOn; + _inkSmoothingManager?.UpdateConfig(); + SaveSettingsToFile(); + } + + private void ComboBoxInkSmoothingQuality_SelectionChanged(object sender, SelectionChangedEventArgs e) { + if (!isLoaded) return; + Settings.Canvas.InkSmoothingQuality = ComboBoxInkSmoothingQuality.SelectedIndex; + _inkSmoothingManager?.UpdateConfig(); + SaveSettingsToFile(); + } + + private void SliderMaxConcurrentTasks_ValueChanged(object sender, RoutedPropertyChangedEventArgs e) { + if (!isLoaded) return; + Settings.Canvas.MaxConcurrentSmoothingTasks = (int)SliderMaxConcurrentTasks.Value; + _inkSmoothingManager?.UpdateConfig(); + SaveSettingsToFile(); + } + + private void ButtonApplyRecommendedSettings_Click(object sender, RoutedEventArgs e) { + // 应用推荐的性能设置 + Helpers.InkSmoothingManager.ApplyRecommendedSettings(); + LoadSettings(false); + _inkSmoothingManager?.UpdateConfig(); + SaveSettingsToFile(); + + ShowNotification("已应用推荐的性能设置"); + } + + private void ButtonShowPerformanceStats_Click(object sender, RoutedEventArgs e) { + if (_inkSmoothingManager != null) + { + var stats = _inkSmoothingManager.GetPerformanceStats(); + ShowNotification($"性能统计: {stats}"); + } + } + */ private void ToggleSwitchAutoSaveStrokesInPowerPoint_Toggled(object sender, RoutedEventArgs e) { if (!isLoaded) return; diff --git a/Ink Canvas/MainWindow_cs/MW_SettingsToLoad.cs b/Ink Canvas/MainWindow_cs/MW_SettingsToLoad.cs index d122a8ae..09b258b8 100644 --- a/Ink Canvas/MainWindow_cs/MW_SettingsToLoad.cs +++ b/Ink Canvas/MainWindow_cs/MW_SettingsToLoad.cs @@ -538,6 +538,23 @@ namespace Ink_Canvas { ToggleSwitchAdvancedBezierSmoothing.IsOn = false; drawingAttributes.FitToCurve = false; } + + // 注释掉新的墨迹平滑性能设置,因为UI控件还没有定义 + /* + // 初始化新的墨迹平滑性能设置 + ToggleSwitchAsyncInkSmoothing.IsOn = Settings.Canvas.UseAsyncInkSmoothing; + ToggleSwitchHardwareAcceleration.IsOn = Settings.Canvas.UseHardwareAcceleration; + ComboBoxInkSmoothingQuality.SelectedIndex = Settings.Canvas.InkSmoothingQuality; + SliderMaxConcurrentTasks.Value = Settings.Canvas.MaxConcurrentSmoothingTasks > 0 ? + Settings.Canvas.MaxConcurrentSmoothingTasks : Environment.ProcessorCount; + + // 检查硬件加速支持 + if (!Helpers.InkSmoothingManager.IsHardwareAccelerationSupported()) + { + ToggleSwitchHardwareAcceleration.IsEnabled = false; + // 可以添加提示文本说明硬件加速不可用 + } + */ // 初始化直线自动拉直相关设置 ToggleSwitchAutoStraightenLine.IsOn = Settings.Canvas.AutoStraightenLine; diff --git a/Ink Canvas/MainWindow_cs/MW_ShapeDrawing.cs b/Ink Canvas/MainWindow_cs/MW_ShapeDrawing.cs index b971ccd9..975c007f 100644 --- a/Ink Canvas/MainWindow_cs/MW_ShapeDrawing.cs +++ b/Ink Canvas/MainWindow_cs/MW_ShapeDrawing.cs @@ -1579,17 +1579,16 @@ namespace Ink_Canvas { { try { - var advancedSmoothing = new Helpers.AdvancedBezierSmoothing - { - }; - // 对临时笔画应用平滑 - if (lastTempStroke != null) + if (lastTempStroke != null && _inkSmoothingManager != null) { - var smoothedStroke = advancedSmoothing.SmoothStroke(lastTempStroke); - inkCanvas.Strokes.Remove(lastTempStroke); - lastTempStroke = smoothedStroke; - inkCanvas.Strokes.Add(smoothedStroke); + var smoothedStroke = _inkSmoothingManager.SmoothStroke(lastTempStroke); + if (smoothedStroke != lastTempStroke) + { + inkCanvas.Strokes.Remove(lastTempStroke); + lastTempStroke = smoothedStroke; + inkCanvas.Strokes.Add(smoothedStroke); + } } } catch (Exception ex) diff --git a/Ink Canvas/MainWindow_cs/MW_SimulatePressure&InkToShape.cs b/Ink Canvas/MainWindow_cs/MW_SimulatePressure&InkToShape.cs index 18499916..6d344408 100644 --- a/Ink Canvas/MainWindow_cs/MW_SimulatePressure&InkToShape.cs +++ b/Ink Canvas/MainWindow_cs/MW_SimulatePressure&InkToShape.cs @@ -2,6 +2,7 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Threading.Tasks; using System.Windows; using System.Windows.Controls; using System.Windows.Ink; @@ -587,18 +588,27 @@ namespace Ink_Canvas { // 检查原始笔画是否仍然存在于画布中 if (inkCanvas.Strokes.Contains(e.Stroke)) { - var advancedSmoothing = new Helpers.AdvancedBezierSmoothing + // 使用新的异步墨迹平滑管理器 + if (Settings.Canvas.UseAsyncInkSmoothing && _inkSmoothingManager != null) { - }; + // 异步处理 + _ = ProcessStrokeAsync(e.Stroke); + } + else + { + // 同步处理(向后兼容) + var smoothedStroke = _inkSmoothingManager?.SmoothStroke(e.Stroke) ?? e.Stroke; - var smoothedStroke = advancedSmoothing.SmoothStroke(e.Stroke); - - // 替换原始笔画 - SetNewBackupOfStroke(); - _currentCommitType = CommitReason.ShapeRecognition; - inkCanvas.Strokes.Remove(e.Stroke); - inkCanvas.Strokes.Add(smoothedStroke); - _currentCommitType = CommitReason.UserInput; + if (smoothedStroke != e.Stroke) + { + // 替换原始笔画 + SetNewBackupOfStroke(); + _currentCommitType = CommitReason.ShapeRecognition; + inkCanvas.Strokes.Remove(e.Stroke); + inkCanvas.Strokes.Add(smoothedStroke); + _currentCommitType = CommitReason.UserInput; + } + } } } catch (Exception ex) @@ -613,6 +623,32 @@ namespace Ink_Canvas { } } + /// + /// 异步处理笔画平滑 + /// + private async Task ProcessStrokeAsync(Stroke originalStroke) + { + try + { + await _inkSmoothingManager.SmoothStrokeAsync(originalStroke, (original, smoothed) => + { + // 在UI线程上执行笔画替换 + if (inkCanvas.Strokes.Contains(original) && smoothed != original) + { + SetNewBackupOfStroke(); + _currentCommitType = CommitReason.ShapeRecognition; + inkCanvas.Strokes.Remove(original); + inkCanvas.Strokes.Add(smoothed); + _currentCommitType = CommitReason.UserInput; + } + }); + } + catch (Exception ex) + { + System.Diagnostics.Debug.WriteLine($"异步墨迹平滑失败: {ex.Message}"); + } + } + // New method: Checks if a stroke is potentially a straight line private bool IsPotentialStraightLine(Stroke stroke) { // 确保有足够的点来进行线条分析 diff --git a/Ink Canvas/Resources/Settings.cs b/Ink Canvas/Resources/Settings.cs index 74ba6e69..f76a5d58 100644 --- a/Ink Canvas/Resources/Settings.cs +++ b/Ink Canvas/Resources/Settings.cs @@ -50,6 +50,14 @@ namespace Ink_Canvas public bool FitToCurve { get; set; } = false; // 默认关闭原来的贝塞尔平滑 [JsonProperty("useAdvancedBezierSmoothing")] public bool UseAdvancedBezierSmoothing { get; set; } = true; // 默认启用高级贝塞尔曲线平滑 + [JsonProperty("useAsyncInkSmoothing")] + public bool UseAsyncInkSmoothing { get; set; } = true; // 默认启用异步墨迹平滑 + [JsonProperty("useHardwareAcceleration")] + public bool UseHardwareAcceleration { get; set; } = true; // 默认启用硬件加速 + [JsonProperty("inkSmoothingQuality")] + public int InkSmoothingQuality { get; set; } = 1; // 0-低质量高性能, 1-平衡, 2-高质量低性能 + [JsonProperty("maxConcurrentSmoothingTasks")] + public int MaxConcurrentSmoothingTasks { get; set; } = 0; // 0表示自动检测CPU核心数 [JsonProperty("clearCanvasAndClearTimeMachine")] public bool ClearCanvasAndClearTimeMachine { get; set; } = false; [JsonProperty("enablePressureTouchMode")] diff --git a/Ink Canvas/obj/Debug/net472/InkCanvasForClass_MarkupCompile.cache b/Ink Canvas/obj/Debug/net472/InkCanvasForClass_MarkupCompile.cache index fc9b1fcc..ad729a64 100644 --- a/Ink Canvas/obj/Debug/net472/InkCanvasForClass_MarkupCompile.cache +++ b/Ink Canvas/obj/Debug/net472/InkCanvasForClass_MarkupCompile.cache @@ -12,7 +12,7 @@ TRACE;DEBUG;NETFRAMEWORK;NET472;;NET30_OR_GREATER;NET35_OR_GREATER;NET40_OR_GREA E:\ICC CE\ICC CE main\community\Ink Canvas\App.xaml 22-2143008179 -77-1409555929 +79-461684434 471037513499 Helpers\Plugins\BuiltIn\SuperLauncher\LauncherSettingsControl.xaml;Helpers\Plugins\BuiltIn\SuperLauncher\LauncherWindow.xaml;MainWindow.xaml;MainWindow_cs\MW_Eraser.xaml;Resources\DrawShapeImageDictionary.xaml;Resources\IconImageDictionary.xaml;Resources\SeewoImageDictionary.xaml;Resources\Styles\Dark.xaml;Resources\Styles\Light.xaml;Windows\AddCustomIconWindow.xaml;Windows\AddPickNameBackgroundWindow.xaml;Windows\CountdownTimerWindow.xaml;Windows\CustomIconWindow.xaml;Windows\CycleProcessBar.xaml;Windows\HasNewUpdateWindow.xaml;Windows\HistoryRollbackWindow.xaml;Windows\ManagePickNameBackgroundsWindow.xaml;Windows\NamesInputWindow.xaml;Windows\OperatingGuideWindow.xaml;Windows\PluginSettingsWindow.xaml;Windows\RandWindow.xaml;Windows\YesOrNoNotificationWindow.xaml;