From b64cefad464c9e945f13181148816848a4166413 Mon Sep 17 00:00:00 2001 From: CJKmkp <2564608840@qq.com> Date: Sat, 30 Aug 2025 22:54:16 +0800 Subject: [PATCH] =?UTF-8?q?improve:=E6=8F=92=E5=85=A5=E5=9B=BE=E7=89=87?= =?UTF-8?q?=E5=8F=8A=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 | 174 ++++++++-- .../HardwareAcceleratedInkProcessor.cs | 56 --- Ink Canvas/Helpers/ImprovedBezierSmoothing.cs | 325 ++++++++++++++++++ Ink Canvas/Helpers/InkSmoothingConfig.cs | 155 +++++++++ Ink Canvas/Helpers/InkSmoothingManager.cs | 6 +- .../MainWindow_cs/MW_ClipboardHandler.cs | 43 +++ .../MainWindow_cs/MW_ElementsControls.cs | 9 +- .../MainWindow_cs/MW_FloatingBarIcons.cs | 109 ++++++ 8 files changed, 786 insertions(+), 91 deletions(-) create mode 100644 Ink Canvas/Helpers/ImprovedBezierSmoothing.cs create mode 100644 Ink Canvas/Helpers/InkSmoothingConfig.cs diff --git a/Ink Canvas/Helpers/AdvancedBezierSmoothing.cs b/Ink Canvas/Helpers/AdvancedBezierSmoothing.cs index 56a2e6f3..a9015cd6 100644 --- a/Ink Canvas/Helpers/AdvancedBezierSmoothing.cs +++ b/Ink Canvas/Helpers/AdvancedBezierSmoothing.cs @@ -7,11 +7,12 @@ using System.Threading.Tasks; using System.Windows.Ink; using System.Windows.Input; using System.Windows.Threading; +using System.Windows; namespace Ink_Canvas.Helpers { /// - /// 异步硬件加速的墨迹平滑处理器 + /// 改进的异步硬件加速墨迹平滑处理器,使用优化的三次贝塞尔曲线拟合 /// public class AsyncAdvancedBezierSmoothing { @@ -26,11 +27,13 @@ namespace Ink_Canvas.Helpers _processingTasks = new ConcurrentDictionary(); } - public double SmoothingStrength { get; set; } = 0.3; // 大幅降低强度 - public double ResampleInterval { get; set; } = 3.0; // 大幅增加间隔减少点数 - public int InterpolationSteps { get; set; } = 8; // 从4增加到8,提高插值步数 + public double SmoothingStrength { get; set; } = 0.4; // 适中的平滑强度 + public double ResampleInterval { get; set; } = 2.5; // 适中的重采样间隔 + public int InterpolationSteps { get; set; } = 12; // 增加插值步数提高精度 public bool UseHardwareAcceleration { get; set; } = true; public int MaxConcurrentTasks { get; set; } = Environment.ProcessorCount; + public bool UseAdaptiveInterpolation { get; set; } = true; // 自适应插值 + public double CurveTension { get; set; } = 0.3; // 曲线张力参数 /// /// 异步平滑笔画 @@ -89,16 +92,16 @@ namespace Ink_Canvas.Helpers cancellationToken.ThrowIfCancellationRequested(); - // 简化处理:只进行轻度平滑,避免点数爆炸 - var smoothedPoints = ApplyLightSmoothing(originalPoints); + // 使用改进的贝塞尔曲线拟合 + var smoothedPoints = ApplyImprovedBezierSmoothing(originalPoints); cancellationToken.ThrowIfCancellationRequested(); - // 确保点数不会过多 - if (smoothedPoints.Length > originalPoints.Length * 2) + // 确保点数合理 + if (smoothedPoints.Length > originalPoints.Length * 3) { - // 如果点数增加太多,回退到原始笔画 - return stroke; + // 如果点数增加太多,进行重采样 + smoothedPoints = ResampleEquidistantOptimized(smoothedPoints, ResampleInterval); } // 创建平滑后的笔画 @@ -111,34 +114,155 @@ namespace Ink_Canvas.Helpers } /// - /// 轻度平滑处理,避免点数爆炸 + /// 改进的贝塞尔曲线平滑处理 /// - private StylusPoint[] ApplyLightSmoothing(StylusPoint[] points) + private StylusPoint[] ApplyImprovedBezierSmoothing(StylusPoint[] points) { - if (points.Length < 3) return points; + if (points.Length < 4) return points; var result = new List(); - result.Add(points[0]); // 保持第一个点 + + // 添加第一个点 + result.Add(points[0]); - // 简单的3点平均平滑 - for (int i = 1; i < points.Length - 1; i++) + // 使用滑动窗口进行贝塞尔曲线拟合 + for (int i = 0; i <= points.Length - 4; i++) { - var prev = points[i - 1]; - var curr = points[i]; - var next = points[i + 1]; + var p0 = points[i]; + var p1 = points[i + 1]; + var p2 = points[i + 2]; + var p3 = points[i + 3]; - // 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; + // 计算改进的控制点 + var controlPoints = CalculateImprovedControlPoints(p0, p1, p2, p3); + + // 自适应插值步数 + int steps = UseAdaptiveInterpolation ? + CalculateAdaptiveSteps(p0, p1, p2, p3) : InterpolationSteps; - result.Add(new StylusPoint(x, y, Math.Max(pressure, 0.1f))); + // 生成贝塞尔曲线点 + for (int j = 1; j <= steps; j++) // 从1开始避免重复第一个点 + { + double t = (double)j / steps; + var bezierPoint = CubicBezierWithControlPoints(controlPoints, t, p0, p3); + result.Add(bezierPoint); + } } - result.Add(points[points.Length - 1]); // 保持最后一个点 + // 添加最后一个点 + result.Add(points[points.Length - 1]); return result.ToArray(); } + + /// + /// 计算改进的控制点 + /// + private (Point cp1, Point cp2) CalculateImprovedControlPoints(StylusPoint p0, StylusPoint p1, StylusPoint p2, StylusPoint p3) + { + // 计算切线方向 + var tangent1 = new Vector(p1.X - p0.X, p1.Y - p0.Y); + var tangent2 = new Vector(p3.X - p2.X, p3.Y - p2.Y); + + // 归一化切线 + if (tangent1.Length > 0) tangent1.Normalize(); + if (tangent2.Length > 0) tangent2.Normalize(); + + // 计算控制点距离(基于点间距离) + double dist1 = Math.Sqrt((p1.X - p0.X) * (p1.X - p0.X) + (p1.Y - p0.Y) * (p1.Y - p0.Y)); + double dist2 = Math.Sqrt((p3.X - p2.X) * (p3.X - p2.X) + (p3.Y - p2.Y) * (p3.Y - p2.Y)); + + double controlDist1 = dist1 * CurveTension; + double controlDist2 = dist2 * CurveTension; + + // 计算控制点 + var cp1 = new Point( + p1.X + tangent1.X * controlDist1, + p1.Y + tangent1.Y * controlDist1 + ); + + var cp2 = new Point( + p2.X - tangent2.X * controlDist2, + p2.Y - tangent2.Y * controlDist2 + ); + + return (cp1, cp2); + } + + /// + /// 自适应插值步数计算 + /// + private int CalculateAdaptiveSteps(StylusPoint p0, StylusPoint p1, StylusPoint p2, StylusPoint p3) + { + // 基于曲线长度和复杂度计算步数 + double totalLength = 0; + totalLength += Math.Sqrt((p1.X - p0.X) * (p1.X - p0.X) + (p1.Y - p0.Y) * (p1.Y - p0.Y)); + totalLength += Math.Sqrt((p2.X - p1.X) * (p2.X - p1.X) + (p2.Y - p1.Y) * (p2.Y - p1.Y)); + totalLength += Math.Sqrt((p3.X - p2.X) * (p3.X - p2.X) + (p3.Y - p2.Y) * (p3.Y - p2.Y)); + + // 计算曲率(简化版本) + double curvature = CalculateCurvature(p0, p1, p2, p3); + + // 基于长度和曲率计算步数 + int baseSteps = Math.Max(8, Math.Min(20, (int)(totalLength / 10))); + int curvatureSteps = (int)(curvature * 10); + + return Math.Max(InterpolationSteps, Math.Min(24, baseSteps + curvatureSteps)); + } + + /// + /// 计算曲率(简化版本) + /// + private double CalculateCurvature(StylusPoint p0, StylusPoint p1, StylusPoint p2, StylusPoint p3) + { + // 计算三个向量的角度变化 + var v1 = new Vector(p1.X - p0.X, p1.Y - p0.Y); + var v2 = new Vector(p2.X - p1.X, p2.Y - p1.Y); + var v3 = new Vector(p3.X - p2.X, p3.Y - p2.Y); + + if (v1.Length == 0 || v2.Length == 0 || v3.Length == 0) return 0; + + v1.Normalize(); + v2.Normalize(); + v3.Normalize(); + + // 计算角度变化 + double angle1 = Math.Acos(Math.Max(-1, Math.Min(1, Vector.Multiply(v1, v2)))); + double angle2 = Math.Acos(Math.Max(-1, Math.Min(1, Vector.Multiply(v2, v3)))); + + return (angle1 + angle2) / Math.PI; // 归一化到0-1 + } + + /// + /// 使用控制点的三次贝塞尔曲线计算 + /// + private StylusPoint CubicBezierWithControlPoints((Point cp1, Point cp2) controlPoints, double t, StylusPoint p0, StylusPoint p3) + { + var p1 = controlPoints.cp1; + var p2 = controlPoints.cp2; + + 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)(p0.PressureFactor * u + p3.PressureFactor * t); + pressure = Math.Max(pressure, 0.1f); + + return new StylusPoint(x, y, pressure); + } + /// /// 硬件加速的向量化指数平滑 /// diff --git a/Ink Canvas/Helpers/HardwareAcceleratedInkProcessor.cs b/Ink Canvas/Helpers/HardwareAcceleratedInkProcessor.cs index ccea253c..1ca4689f 100644 --- a/Ink Canvas/Helpers/HardwareAcceleratedInkProcessor.cs +++ b/Ink Canvas/Helpers/HardwareAcceleratedInkProcessor.cs @@ -197,61 +197,5 @@ namespace Ink_Canvas.Helpers } } - /// - /// 质量配置枚举 - /// - public enum InkSmoothingQuality - { - HighPerformance = 0, // 高性能低质量 - Balanced = 1, // 平衡 - HighQuality = 2 // 高质量低性能 - } - /// - /// 墨迹平滑配置 - /// - public class InkSmoothingConfig - { - public InkSmoothingQuality Quality { get; set; } = InkSmoothingQuality.HighQuality; - 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.8; // 高质量模式的平滑强度 - public double ResampleInterval { get; set; } = 0.8; // 高质量模式的重采样间隔 - public int InterpolationSteps { get; set; } = 64; // 高质量模式的插值步数 - - 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 = 16; - break; - case InkSmoothingQuality.Balanced: - SmoothingStrength = 0.6; - ResampleInterval = 1.2; - InterpolationSteps = 32; - break; - case InkSmoothingQuality.HighQuality: - SmoothingStrength = 0.8; - ResampleInterval = 0.8; - InterpolationSteps = 64; - break; - } - } - } } diff --git a/Ink Canvas/Helpers/ImprovedBezierSmoothing.cs b/Ink Canvas/Helpers/ImprovedBezierSmoothing.cs new file mode 100644 index 00000000..f249885d --- /dev/null +++ b/Ink Canvas/Helpers/ImprovedBezierSmoothing.cs @@ -0,0 +1,325 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Windows; +using System.Windows.Ink; +using System.Windows.Input; + +namespace Ink_Canvas.Helpers +{ + /// + /// 改进的三次贝塞尔曲线平滑算法 + /// + public class ImprovedBezierSmoothing + { + private readonly InkSmoothingConfig _config; + + public ImprovedBezierSmoothing(InkSmoothingConfig config = null) + { + _config = config ?? new InkSmoothingConfig(); + } + + /// + /// 使用改进的贝塞尔曲线算法平滑笔画 + /// + public Stroke SmoothStroke(Stroke originalStroke) + { + if (originalStroke == null || originalStroke.StylusPoints.Count < 3) + return originalStroke; + + var originalPoints = originalStroke.StylusPoints.ToArray(); + + // 预处理:去除噪声点 + var cleanedPoints = RemoveNoisePoints(originalPoints); + + // 使用改进的贝塞尔曲线拟合 + var smoothedPoints = ApplyCubicBezierSmoothing(cleanedPoints); + + // 后处理:重采样和优化 + var finalPoints = PostProcessPoints(smoothedPoints); + + return new Stroke(new StylusPointCollection(finalPoints)) + { + DrawingAttributes = originalStroke.DrawingAttributes.Clone() + }; + } + + /// + /// 去除噪声点 + /// + private StylusPoint[] RemoveNoisePoints(StylusPoint[] points) + { + if (points.Length < 3) return points; + + var result = new List { points[0] }; + double minDistance = _config.ResampleInterval * 0.5; + + for (int i = 1; i < points.Length - 1; i++) + { + var prev = result[result.Count - 1]; + var curr = points[i]; + var next = points[i + 1]; + + // 计算到前一个点的距离 + double distToPrev = Math.Sqrt((curr.X - prev.X) * (curr.X - prev.X) + + (curr.Y - prev.Y) * (curr.Y - prev.Y)); + + // 如果距离太近,跳过这个点 + if (distToPrev < minDistance) + continue; + + // 检查是否为异常点(与前后点形成锐角) + if (IsOutlierPoint(prev, curr, next)) + continue; + + result.Add(curr); + } + + result.Add(points[points.Length - 1]); + return result.ToArray(); + } + + /// + /// 检查是否为异常点 + /// + private bool IsOutlierPoint(StylusPoint prev, StylusPoint curr, StylusPoint next) + { + var v1 = new Vector(curr.X - prev.X, curr.Y - prev.Y); + var v2 = new Vector(next.X - curr.X, next.Y - curr.Y); + + if (v1.Length == 0 || v2.Length == 0) return false; + + v1.Normalize(); + v2.Normalize(); + + double dotProduct = Vector.Multiply(v1, v2); + double angle = Math.Acos(Math.Max(-1, Math.Min(1, dotProduct))); + + // 如果角度小于30度,认为是异常点 + return angle < Math.PI / 6; + } + + /// + /// 应用三次贝塞尔曲线平滑 + /// + private StylusPoint[] ApplyCubicBezierSmoothing(StylusPoint[] points) + { + if (points.Length < 4) return points; + + var result = new List(); + result.Add(points[0]); + + // 使用滑动窗口进行贝塞尔曲线拟合 + for (int i = 0; i <= points.Length - 4; i++) + { + var p0 = points[i]; + var p1 = points[i + 1]; + var p2 = points[i + 2]; + var p3 = points[i + 3]; + + // 计算控制点 + var controlPoints = CalculateOptimalControlPoints(p0, p1, p2, p3); + + // 计算插值步数 + int steps = CalculateInterpolationSteps(p0, p1, p2, p3); + + // 生成贝塞尔曲线点 + for (int j = 1; j <= steps; j++) + { + double t = (double)j / steps; + var bezierPoint = CalculateBezierPoint(p0, controlPoints.cp1, controlPoints.cp2, p3, t); + result.Add(bezierPoint); + } + } + + result.Add(points[points.Length - 1]); + return result.ToArray(); + } + + /// + /// 计算最优控制点 + /// + private (Point cp1, Point cp2) CalculateOptimalControlPoints(StylusPoint p0, StylusPoint p1, StylusPoint p2, StylusPoint p3) + { + // 计算切线方向 + var tangent1 = CalculateTangent(p0, p1, p2); + var tangent2 = CalculateTangent(p1, p2, p3); + + // 计算控制点距离 + double dist1 = CalculateDistance(p0, p1); + double dist2 = CalculateDistance(p2, p3); + + double controlDist1 = dist1 * _config.CurveTension; + double controlDist2 = dist2 * _config.CurveTension; + + // 计算控制点 + var cp1 = new Point( + p1.X + tangent1.X * controlDist1, + p1.Y + tangent1.Y * controlDist1 + ); + + var cp2 = new Point( + p2.X - tangent2.X * controlDist2, + p2.Y - tangent2.Y * controlDist2 + ); + + return (cp1, cp2); + } + + /// + /// 计算切线方向 + /// + private Vector CalculateTangent(StylusPoint p0, StylusPoint p1, StylusPoint p2) + { + var v1 = new Vector(p1.X - p0.X, p1.Y - p0.Y); + var v2 = new Vector(p2.X - p1.X, p2.Y - p1.Y); + + // 如果向量长度为零,返回零向量 + if (v1.Length == 0 || v2.Length == 0) + return new Vector(0, 0); + + v1.Normalize(); + v2.Normalize(); + + // 返回平均方向 + var tangent = (v1 + v2) / 2; + if (tangent.Length > 0) + tangent.Normalize(); + + return tangent; + } + + /// + /// 计算两点间距离 + /// + private double CalculateDistance(StylusPoint p1, StylusPoint p2) + { + double dx = p2.X - p1.X; + double dy = p2.Y - p1.Y; + return Math.Sqrt(dx * dx + dy * dy); + } + + /// + /// 计算插值步数 + /// + private int CalculateInterpolationSteps(StylusPoint p0, StylusPoint p1, StylusPoint p2, StylusPoint p3) + { + if (!_config.UseAdaptiveInterpolation) + return _config.InterpolationSteps; + + // 计算曲线长度 + double totalLength = CalculateDistance(p0, p1) + CalculateDistance(p1, p2) + CalculateDistance(p2, p3); + + // 计算曲率 + double curvature = CalculateCurvature(p0, p1, p2, p3); + + // 基于长度和曲率计算步数 + int baseSteps = Math.Max(8, Math.Min(20, (int)(totalLength / 10))); + int curvatureSteps = (int)(curvature * 15); + + return Math.Max(_config.InterpolationSteps, Math.Min(30, baseSteps + curvatureSteps)); + } + + /// + /// 计算曲率 + /// + private double CalculateCurvature(StylusPoint p0, StylusPoint p1, StylusPoint p2, StylusPoint p3) + { + var v1 = new Vector(p1.X - p0.X, p1.Y - p0.Y); + var v2 = new Vector(p2.X - p1.X, p2.Y - p1.Y); + var v3 = new Vector(p3.X - p2.X, p3.Y - p2.Y); + + if (v1.Length == 0 || v2.Length == 0 || v3.Length == 0) return 0; + + v1.Normalize(); + v2.Normalize(); + v3.Normalize(); + + // 计算角度变化 + double angle1 = Math.Acos(Math.Max(-1, Math.Min(1, Vector.Multiply(v1, v2)))); + double angle2 = Math.Acos(Math.Max(-1, Math.Min(1, Vector.Multiply(v2, v3)))); + + return (angle1 + angle2) / Math.PI; // 归一化到0-1 + } + + /// + /// 计算贝塞尔曲线上的点 + /// + private StylusPoint CalculateBezierPoint(StylusPoint p0, Point cp1, Point cp2, 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 * cp1.X + c2 * cp2.X + c3 * p3.X; + double y = c0 * p0.Y + c1 * cp1.Y + c2 * cp2.Y + c3 * p3.Y; + + // 插值压力值 + float pressure = (float)(p0.PressureFactor * u + p3.PressureFactor * t); + pressure = Math.Max(pressure, 0.1f); + + return new StylusPoint(x, y, pressure); + } + + /// + /// 后处理点集 + /// + private StylusPoint[] PostProcessPoints(StylusPoint[] points) + { + if (points.Length == 0) return points; + + // 如果点数过多,进行重采样 + if (points.Length > _config.MaxPointsPerStroke) + { + return ResamplePoints(points, _config.ResampleInterval); + } + + return points; + } + + /// + /// 重采样点集 + /// + private StylusPoint[] ResamplePoints(StylusPoint[] points, double interval) + { + var result = new List { 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(); + } + } +} \ No newline at end of file diff --git a/Ink Canvas/Helpers/InkSmoothingConfig.cs b/Ink Canvas/Helpers/InkSmoothingConfig.cs new file mode 100644 index 00000000..e57aad38 --- /dev/null +++ b/Ink Canvas/Helpers/InkSmoothingConfig.cs @@ -0,0 +1,155 @@ +using System; +using System.Configuration; + +namespace Ink_Canvas.Helpers +{ + /// + /// 墨迹平滑配置类 + /// + public class InkSmoothingConfig + { + // 基本平滑参数 + public double SmoothingStrength { get; set; } = 0.4; + public double ResampleInterval { get; set; } = 2.5; + public int InterpolationSteps { get; set; } = 12; + + // 贝塞尔曲线参数 + public bool UseAdaptiveInterpolation { get; set; } = true; + public double CurveTension { get; set; } = 0.3; + public double MinCurvatureThreshold { get; set; } = 0.1; + public double MaxCurvatureThreshold { get; set; } = 0.8; + + // 性能参数 + public bool UseHardwareAcceleration { get; set; } = true; + public bool UseAsyncProcessing { get; set; } = true; + public int MaxConcurrentTasks { get; set; } = Environment.ProcessorCount; + public int MaxPointsPerStroke { get; set; } = 10000; + + // 质量设置 + public SmoothingQuality Quality { get; set; } = SmoothingQuality.Balanced; + + public enum SmoothingQuality + { + Performance, // 性能优先 + Balanced, // 平衡 + Quality // 质量优先 + } + + // 兼容性枚举 + public enum InkSmoothingQuality + { + HighPerformance = 0, // 高性能低质量 + Balanced = 1, // 平衡 + HighQuality = 2 // 高质量低性能 + } + + /// + /// 从设置中加载配置 + /// + public static InkSmoothingConfig FromSettings() + { + var config = new InkSmoothingConfig(); + + try + { + // 尝试从MainWindow.Settings加载配置(兼容性) + if (MainWindow.Settings?.Canvas != null) + { + config.Quality = (SmoothingQuality)MainWindow.Settings.Canvas.InkSmoothingQuality; + config.UseHardwareAcceleration = MainWindow.Settings.Canvas.UseHardwareAcceleration; + config.UseAsyncProcessing = MainWindow.Settings.Canvas.UseAsyncInkSmoothing; + config.MaxConcurrentTasks = MainWindow.Settings.Canvas.MaxConcurrentSmoothingTasks > 0 ? + MainWindow.Settings.Canvas.MaxConcurrentSmoothingTasks : Environment.ProcessorCount; + } + } + catch (Exception ex) + { + System.Diagnostics.Debug.WriteLine($"加载平滑配置失败: {ex.Message}"); + } + + return config; + } + + /// + /// 应用质量设置 + /// + public void ApplyQualitySettings() + { + switch (Quality) + { + case SmoothingQuality.Performance: + SmoothingStrength = 0.2; + ResampleInterval = 4.0; + InterpolationSteps = 6; + UseAdaptiveInterpolation = false; + CurveTension = 0.2; + MaxConcurrentTasks = Math.Max(1, Environment.ProcessorCount / 2); + break; + + case SmoothingQuality.Balanced: + SmoothingStrength = 0.4; + ResampleInterval = 2.5; + InterpolationSteps = 12; + UseAdaptiveInterpolation = true; + CurveTension = 0.3; + MaxConcurrentTasks = Environment.ProcessorCount; + break; + + case SmoothingQuality.Quality: + SmoothingStrength = 0.6; + ResampleInterval = 1.5; + InterpolationSteps = 20; + UseAdaptiveInterpolation = true; + CurveTension = 0.4; + MaxConcurrentTasks = Environment.ProcessorCount; + break; + } + } + + /// + /// 保存配置到设置 + /// + public void SaveToSettings() + { + try + { + // 尝试保存到MainWindow.Settings(兼容性) + if (MainWindow.Settings?.Canvas != null) + { + MainWindow.Settings.Canvas.InkSmoothingQuality = (int)Quality; + MainWindow.Settings.Canvas.UseHardwareAcceleration = UseHardwareAcceleration; + MainWindow.Settings.Canvas.UseAsyncInkSmoothing = UseAsyncProcessing; + MainWindow.Settings.Canvas.MaxConcurrentSmoothingTasks = MaxConcurrentTasks; + + } + } + catch (Exception ex) + { + System.Diagnostics.Debug.WriteLine($"保存平滑配置失败: {ex.Message}"); + } + } + + /// + /// 验证配置参数 + /// + public bool Validate() + { + return SmoothingStrength >= 0.0 && SmoothingStrength <= 1.0 && + ResampleInterval > 0.0 && + InterpolationSteps > 0 && InterpolationSteps <= 50 && + CurveTension >= 0.0 && CurveTension <= 1.0 && + MaxConcurrentTasks > 0 && + MaxPointsPerStroke > 0; + } + + /// + /// 获取配置摘要 + /// + public string GetSummary() + { + return $"质量: {Quality}, 强度: {SmoothingStrength:F2}, 间隔: {ResampleInterval:F1}, " + + $"步数: {InterpolationSteps}, 自适应: {UseAdaptiveInterpolation}, " + + $"张力: {CurveTension:F2}, 硬件加速: {UseHardwareAcceleration}"; + } + } +} \ No newline at end of file diff --git a/Ink Canvas/Helpers/InkSmoothingManager.cs b/Ink Canvas/Helpers/InkSmoothingManager.cs index 3d2e5db5..489cdda3 100644 --- a/Ink Canvas/Helpers/InkSmoothingManager.cs +++ b/Ink Canvas/Helpers/InkSmoothingManager.cs @@ -197,7 +197,7 @@ namespace Ink_Canvas.Helpers if (processorCount >= 4 && isHardwareAccelerated) { // 降低高质量模式的门槛,4核以上且支持硬件加速就使用高质量 - config.Quality = InkSmoothingQuality.HighQuality; + config.Quality = (InkSmoothingConfig.SmoothingQuality)InkSmoothingConfig.InkSmoothingQuality.HighQuality; config.UseHardwareAcceleration = true; config.UseAsyncProcessing = true; config.MaxConcurrentTasks = Math.Min(processorCount, 8); @@ -205,7 +205,7 @@ namespace Ink_Canvas.Helpers else if (processorCount >= 2) { // 2核以上使用平衡模式 - config.Quality = InkSmoothingQuality.Balanced; + config.Quality = (InkSmoothingConfig.SmoothingQuality)InkSmoothingConfig.InkSmoothingQuality.Balanced; config.UseHardwareAcceleration = isHardwareAccelerated; config.UseAsyncProcessing = true; config.MaxConcurrentTasks = Math.Min(processorCount, 4); @@ -213,7 +213,7 @@ namespace Ink_Canvas.Helpers else { // 单核或性能较低的设备使用高性能模式 - config.Quality = InkSmoothingQuality.HighPerformance; + config.Quality = (InkSmoothingConfig.SmoothingQuality)InkSmoothingConfig.InkSmoothingQuality.HighPerformance; config.UseHardwareAcceleration = false; config.UseAsyncProcessing = false; config.MaxConcurrentTasks = 1; diff --git a/Ink Canvas/MainWindow_cs/MW_ClipboardHandler.cs b/Ink Canvas/MainWindow_cs/MW_ClipboardHandler.cs index 6a136cd3..0db312ab 100644 --- a/Ink Canvas/MainWindow_cs/MW_ClipboardHandler.cs +++ b/Ink Canvas/MainWindow_cs/MW_ClipboardHandler.cs @@ -3,8 +3,10 @@ using System; using System.Threading.Tasks; using System.Windows; using System.Windows.Controls; +using System.Windows.Ink; using System.Windows.Input; using System.Windows.Media.Imaging; +using System.Windows.Media; namespace Ink_Canvas { @@ -129,6 +131,16 @@ namespace Ink_Canvas string timestamp = "img_clipboard_" + DateTime.Now.ToString("yyyyMMdd_HH_mm_ss_fff"); image.Name = timestamp; + // 初始化TransformGroup + if (image is FrameworkElement element) + { + var transformGroup = new TransformGroup(); + transformGroup.Children.Add(new ScaleTransform(1, 1)); + transformGroup.Children.Add(new TranslateTransform(0, 0)); + transformGroup.Children.Add(new RotateTransform(0)); + element.RenderTransform = transformGroup; + } + // 设置位置 if (position.HasValue) { @@ -142,9 +154,40 @@ namespace Ink_Canvas CenterAndScaleElement(image); } + // 设置图片属性,避免被InkCanvas选择系统处理 + image.IsHitTestVisible = true; + image.Focusable = false; + + // 初始化InkCanvas选择设置 + if (inkCanvas != null) + { + // 清除当前选择,避免显示控制点 + inkCanvas.Select(new StrokeCollection()); + // 设置编辑模式为非选择模式 + inkCanvas.EditingMode = InkCanvasEditingMode.None; + } + // 添加到画布 inkCanvas.Children.Add(image); + // 绑定事件处理器 + if (image is FrameworkElement elementForEvents) + { + // 鼠标事件 + elementForEvents.MouseLeftButtonDown += Element_MouseLeftButtonDown; + elementForEvents.MouseLeftButtonUp += Element_MouseLeftButtonUp; + elementForEvents.MouseMove += Element_MouseMove; + elementForEvents.MouseWheel += Element_MouseWheel; + + // 触摸事件 + elementForEvents.IsManipulationEnabled = true; + elementForEvents.ManipulationDelta += Element_ManipulationDelta; + elementForEvents.ManipulationCompleted += Element_ManipulationCompleted; + + // 设置光标 + elementForEvents.Cursor = Cursors.Hand; + } + // 提交到历史记录 timeMachine.CommitElementInsertHistory(image); diff --git a/Ink Canvas/MainWindow_cs/MW_ElementsControls.cs b/Ink Canvas/MainWindow_cs/MW_ElementsControls.cs index 37c553f6..4c2e89c0 100644 --- a/Ink Canvas/MainWindow_cs/MW_ElementsControls.cs +++ b/Ink Canvas/MainWindow_cs/MW_ElementsControls.cs @@ -906,15 +906,10 @@ namespace Ink_Canvas { if (inkCanvas != null) { - // 隐藏选择控制点 - 通过清除选择和设置编辑模式 + // 清除当前选择,避免显示控制点 inkCanvas.Select(new StrokeCollection()); + // 设置编辑模式为非选择模式 inkCanvas.EditingMode = InkCanvasEditingMode.None; - - // 隐藏选择框 - if (GridInkCanvasSelectionCover != null) - { - GridInkCanvasSelectionCover.Visibility = Visibility.Collapsed; - } } } } diff --git a/Ink Canvas/MainWindow_cs/MW_FloatingBarIcons.cs b/Ink Canvas/MainWindow_cs/MW_FloatingBarIcons.cs index a3ba95ea..f6fe9c6f 100644 --- a/Ink Canvas/MainWindow_cs/MW_FloatingBarIcons.cs +++ b/Ink Canvas/MainWindow_cs/MW_FloatingBarIcons.cs @@ -15,6 +15,7 @@ using System.Windows.Media.Animation; using System.Windows.Media.Imaging; using Application = System.Windows.Application; using Button = System.Windows.Controls.Button; +using Cursors = System.Windows.Input.Cursors; using HorizontalAlignment = System.Windows.HorizontalAlignment; using Image = System.Windows.Controls.Image; using MessageBox = iNKORE.UI.WPF.Modern.Controls.MessageBox; @@ -2828,6 +2829,72 @@ namespace Ink_Canvas } } + // 新增:插入图片方法 + private async void InsertImage_MouseUp_New(object sender, MouseButtonEventArgs e) + { + var dialog = new OpenFileDialog + { + Filter = "图片文件|*.jpg;*.jpeg;*.png;*.bmp;*.gif" + }; + if (dialog.ShowDialog() == true) + { + string filePath = dialog.FileName; + Image image = await CreateAndCompressImageAsync(filePath); + if (image != null) + { + string timestamp = "img_" + DateTime.Now.ToString("yyyyMMdd_HH_mm_ss_fff"); + image.Name = timestamp; + + // 初始化TransformGroup + if (image is FrameworkElement element) + { + var transformGroup = new TransformGroup(); + transformGroup.Children.Add(new ScaleTransform(1, 1)); + transformGroup.Children.Add(new TranslateTransform(0, 0)); + transformGroup.Children.Add(new RotateTransform(0)); + element.RenderTransform = transformGroup; + } + + CenterAndScaleElement(image); + + // 设置图片属性,避免被InkCanvas选择系统处理 + image.IsHitTestVisible = true; + image.Focusable = false; + + // 初始化InkCanvas选择设置 + if (inkCanvas != null) + { + // 清除当前选择,避免显示控制点 + inkCanvas.Select(new StrokeCollection()); + // 设置编辑模式为非选择模式 + inkCanvas.EditingMode = InkCanvasEditingMode.None; + } + + inkCanvas.Children.Add(image); + + // 绑定事件处理器 + if (image is FrameworkElement elementForEvents) + { + // 鼠标事件 + elementForEvents.MouseLeftButtonDown += Element_MouseLeftButtonDown; + elementForEvents.MouseLeftButtonUp += Element_MouseLeftButtonUp; + elementForEvents.MouseMove += Element_MouseMove; + elementForEvents.MouseWheel += Element_MouseWheel; + + // 触摸事件 + elementForEvents.IsManipulationEnabled = true; + elementForEvents.ManipulationDelta += Element_ManipulationDelta; + elementForEvents.ManipulationCompleted += Element_ManipulationCompleted; + + // 设置光标 + elementForEvents.Cursor = Cursors.Hand; + } + + timeMachine.CommitElementInsertHistory(image); + } + } + } + // Keep the old method for backward compatibility private async void InsertImage_MouseUp(object sender, MouseButtonEventArgs e) { @@ -2844,9 +2911,51 @@ namespace Ink_Canvas string timestamp = "img_" + DateTime.Now.ToString("yyyyMMdd_HH_mm_ss_fff"); image.Name = timestamp; + // 初始化TransformGroup + if (image is FrameworkElement element) + { + var transformGroup = new TransformGroup(); + transformGroup.Children.Add(new ScaleTransform(1, 1)); + transformGroup.Children.Add(new TranslateTransform(0, 0)); + transformGroup.Children.Add(new RotateTransform(0)); + element.RenderTransform = transformGroup; + } + CenterAndScaleElement(image); + + // 设置图片属性,避免被InkCanvas选择系统处理 + image.IsHitTestVisible = true; + image.Focusable = false; + + // 初始化InkCanvas选择设置 + if (inkCanvas != null) + { + // 清除当前选择,避免显示控制点 + inkCanvas.Select(new StrokeCollection()); + // 设置编辑模式为非选择模式 + inkCanvas.EditingMode = InkCanvasEditingMode.None; + } + inkCanvas.Children.Add(image); + // 绑定事件处理器 + if (image is FrameworkElement elementForEvents) + { + // 鼠标事件 + elementForEvents.MouseLeftButtonDown += Element_MouseLeftButtonDown; + elementForEvents.MouseLeftButtonUp += Element_MouseLeftButtonUp; + elementForEvents.MouseMove += Element_MouseMove; + elementForEvents.MouseWheel += Element_MouseWheel; + + // 触摸事件 + elementForEvents.IsManipulationEnabled = true; + elementForEvents.ManipulationDelta += Element_ManipulationDelta; + elementForEvents.ManipulationCompleted += Element_ManipulationCompleted; + + // 设置光标 + elementForEvents.Cursor = Cursors.Hand; + } + timeMachine.CommitElementInsertHistory(image); } }