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);
}
}