From 66afe271c57e5dcf8ee17e1e6c302c72460353f3 Mon Sep 17 00:00:00 2001 From: CJKmkp <2564608840@qq.com> Date: Sat, 4 Apr 2026 22:56:34 +0800 Subject: [PATCH] =?UTF-8?q?improve:=E5=AE=9E=E6=97=B6=E7=AC=94=E9=94=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Ink Canvas/Helpers/MultiTouchInput.cs | 26 ++++-- .../MW_SimulatePressure&InkToShape.cs | 92 ++++++++++++------- Ink Canvas/MainWindow_cs/MW_TouchEvents.cs | 18 +++- Ink Canvas/Resources/Settings.cs | 2 +- 4 files changed, 98 insertions(+), 40 deletions(-) diff --git a/Ink Canvas/Helpers/MultiTouchInput.cs b/Ink Canvas/Helpers/MultiTouchInput.cs index 40391fc0..f1a745b1 100644 --- a/Ink Canvas/Helpers/MultiTouchInput.cs +++ b/Ink Canvas/Helpers/MultiTouchInput.cs @@ -111,6 +111,14 @@ namespace Ink_Canvas.Helpers /// /// 绘制点段到新的DrawingVisual /// + private static double PressureToVisualScale(float pressureFactor, bool ignorePressure) + { + if (ignorePressure) + return 1.0; + // 与 WPF 墨迹观感接近:0.5 为标称,压低变细、抬高变粗(预览此前固定 Pen 宽,等同忽略压感) + return Math.Max(0.22, Math.Min(2.1, 0.42 + 1.16 * pressureFactor)); + } + private void DrawSegmentToNewVisual(int startIndex, int endIndex) { if (Stroke == null || Stroke.StylusPoints.Count == 0 || _visualCanvas == null) return; @@ -118,6 +126,7 @@ namespace Ink_Canvas.Helpers var points = Stroke.StylusPoints; var drawingAttributes = Stroke.DrawingAttributes; + var ignorePressure = drawingAttributes.IgnorePressure; // 创建新的DrawingVisual用于绘制这个点段 var segmentVisual = new DrawingVisual(); @@ -128,11 +137,6 @@ namespace Ink_Canvas.Helpers using (var dc = segmentVisual.RenderOpen()) { - var pen = new Pen(new SolidColorBrush(drawingAttributes.Color), drawingAttributes.Width); - pen.StartLineCap = PenLineCap.Round; - pen.EndLineCap = PenLineCap.Round; - pen.LineJoin = PenLineJoin.Round; - // 绘制指定范围内的点段 if (endIndex - startIndex >= 2) { @@ -141,6 +145,15 @@ namespace Ink_Canvas.Helpers { var startPoint = new Point(points[i].X, points[i].Y); var endPoint = new Point(points[i + 1].X, points[i + 1].Y); + var s0 = PressureToVisualScale(points[i].PressureFactor, ignorePressure); + var s1 = PressureToVisualScale(points[i + 1].PressureFactor, ignorePressure); + var thickness = Math.Max(0.35, (drawingAttributes.Width * s0 + drawingAttributes.Width * s1) / 2.0); + var pen = new Pen(new SolidColorBrush(drawingAttributes.Color), thickness) + { + StartLineCap = PenLineCap.Round, + EndLineCap = PenLineCap.Round, + LineJoin = PenLineJoin.Round + }; dc.DrawLine(pen, startPoint, endPoint); } } @@ -149,8 +162,9 @@ namespace Ink_Canvas.Helpers // 只有一个点,绘制圆点 var brush = new SolidColorBrush(drawingAttributes.Color); var point = points[startIndex]; + var s = PressureToVisualScale(point.PressureFactor, ignorePressure); dc.DrawEllipse(brush, null, new Point(point.X, point.Y), - drawingAttributes.Width / 2, drawingAttributes.Height / 2); + drawingAttributes.Width * s / 2, drawingAttributes.Height * s / 2); } } diff --git a/Ink Canvas/MainWindow_cs/MW_SimulatePressure&InkToShape.cs b/Ink Canvas/MainWindow_cs/MW_SimulatePressure&InkToShape.cs index f0220b3c..48155dcf 100644 --- a/Ink Canvas/MainWindow_cs/MW_SimulatePressure&InkToShape.cs +++ b/Ink Canvas/MainWindow_cs/MW_SimulatePressure&InkToShape.cs @@ -180,16 +180,12 @@ namespace Ink_Canvas var speed = GetPointSpeed(e.Stroke.StylusPoints[Math.Max(i - 1, 0)].ToPoint(), e.Stroke.StylusPoints[i].ToPoint(), e.Stroke.StylusPoints[Math.Min(i + 1, n)].ToPoint()); - var point = new StylusPoint(); - if (speed >= 0.25) - point.PressureFactor = (float)(0.5 - 0.3 * (Math.Min(speed, 1.5) - 0.3) / 1.2); - else if (speed >= 0.05) - point.PressureFactor = (float)0.5; - else - point.PressureFactor = (float)(0.5 + 0.4 * (0.05 - speed) / 0.05); - - point.X = e.Stroke.StylusPoints[i].X; - point.Y = e.Stroke.StylusPoints[i].Y; + var point = new StylusPoint + { + PressureFactor = RateBasedPressureFactorFromPointSpeed(speed), + X = e.Stroke.StylusPoints[i].X, + Y = e.Stroke.StylusPoints[i].Y + }; stylusPoints.Add(point); } @@ -423,16 +419,12 @@ namespace Ink_Canvas var speed = GetPointSpeed(e.Stroke.StylusPoints[Math.Max(i - 1, 0)].ToPoint(), e.Stroke.StylusPoints[i].ToPoint(), e.Stroke.StylusPoints[Math.Min(i + 1, n)].ToPoint()); - var point = new StylusPoint(); - if (speed >= 0.25) - point.PressureFactor = (float)(0.5 - 0.3 * (Math.Min(speed, 1.5) - 0.3) / 1.2); - else if (speed >= 0.05) - point.PressureFactor = (float)0.5; - else - point.PressureFactor = (float)(0.5 + 0.4 * (0.05 - speed) / 0.05); - - point.X = e.Stroke.StylusPoints[i].X; - point.Y = e.Stroke.StylusPoints[i].Y; + var point = new StylusPoint + { + PressureFactor = RateBasedPressureFactorFromPointSpeed(speed), + X = e.Stroke.StylusPoints[i].X, + Y = e.Stroke.StylusPoints[i].Y + }; stylusPoints.Add(point); } @@ -498,7 +490,6 @@ namespace Ink_Canvas // 会导致不进入逻辑或进入后渲染仍忽略 PressureFactor;具体在 ApplyVelocityBrushTipFromSpeed 内关闭。 if (Settings.Canvas.InkStyle == 3 && !touchPressureSimulationApplied - && !Settings.Canvas.DisablePressure && penType != 1 && e.Stroke?.DrawingAttributes != null && !e.Stroke.DrawingAttributes.IsHighlighter @@ -2124,8 +2115,49 @@ namespace Ink_Canvas / 20; } + private static float RateBasedPressureFactorFromPointSpeed(double speed) + { + if (speed >= 0.25) + return (float)(0.5 - 0.3 * (Math.Min(speed, 1.5) - 0.3) / 1.2); + if (speed >= 0.05) + return 0.5f; + return (float)(0.5 + 0.4 * (0.05 - speed) / 0.05); + } + + private static float RealtimeBrushTipMixRatePressureFromSpeed(double speed) + { + if (speed < 0) speed = 0; + const double slowRef = 0.012; + const double fastRef = 0.5; + var t = (speed - slowRef) / (fastRef - slowRef); + if (t < 0) t = 0; + else if (t > 1) t = 1; + t = t * t * (3.0 - 2.0 * t); + const double pThick = 0.9; + const double pThin = 0.22; + var p = pThick + (pThin - pThick) * t; + return (float)Math.Max(0.08, Math.Min(1.0, p)); + } + + private static bool IsStrokePressureApproximatelyConstant(StylusPointCollection pts, out float meanPf) + { + meanPf = 0.5f; + if (pts == null || pts.Count == 0) return true; + double sum = 0; + foreach (StylusPoint p in pts) sum += p.PressureFactor; + meanPf = (float)(sum / pts.Count); + const float tol = 0.04f; + foreach (StylusPoint p in pts) + { + if (Math.Abs(p.PressureFactor - meanPf) > tol) + return false; + } + + return true; + } + /// - /// 将沿线速度映射为压感并与硬件压感混合,快写略细、慢写略粗;与 Inkeys 中 RTSSpeed 驱动的笔锋类似,在落笔后统一施加。 + /// 将沿线速度映射为压感并与硬件压感混合,快写略细、慢写略粗;在落笔时(及手写笔移动时由调用方)统一施加。 /// 无压感设备上系统可能将 置为 true,此处强制关闭以便粗细随合成压感变化(与「屏蔽压感」无关:调用方已保证未屏蔽)。 /// private void ApplyVelocityBrushTipFromSpeed(Stroke stroke) @@ -2142,6 +2174,10 @@ namespace Ink_Canvas var pts = stroke.StylusPoints; if (pts.Count < 3) return; + var effectiveMix = (float)mix; + if (IsStrokePressureApproximatelyConstant(pts, out _)) + effectiveMix = Math.Max(effectiveMix, 0.78f); + var n = pts.Count - 1; var stylusPoints = new StylusPointCollection(); @@ -2152,18 +2188,10 @@ namespace Ink_Canvas pts[i].ToPoint(), pts[Math.Min(i + 1, n)].ToPoint()); - float speedPressure; - if (speed >= 0.25) - speedPressure = (float)(0.5 - 0.3 * (Math.Min(speed, 1.5) - 0.3) / 1.2); - else if (speed >= 0.05) - speedPressure = 0.5f; - else - speedPressure = (float)(0.5 + 0.4 * (0.05 - speed) / 0.05); - - speedPressure = (float)Math.Max(0.08, Math.Min(1.0, speedPressure)); + var speedPressure = RealtimeBrushTipMixRatePressureFromSpeed(speed); var basePf = pts[i].PressureFactor; - var blended = (float)((1.0 - mix) * basePf + mix * speedPressure); + var blended = (1.0f - effectiveMix) * basePf + effectiveMix * speedPressure; blended = (float)Math.Max(0.08, Math.Min(1.0, blended)); var p = new StylusPoint(pts[i].X, pts[i].Y, blended); diff --git a/Ink Canvas/MainWindow_cs/MW_TouchEvents.cs b/Ink Canvas/MainWindow_cs/MW_TouchEvents.cs index 48f46d7e..7f44331e 100644 --- a/Ink Canvas/MainWindow_cs/MW_TouchEvents.cs +++ b/Ink Canvas/MainWindow_cs/MW_TouchEvents.cs @@ -542,7 +542,23 @@ namespace Ink_Canvas var stylusPointCollection = e.GetStylusPoints(this); foreach (var stylusPoint in stylusPointCollection) strokeVisual.Add(new StylusPoint(stylusPoint.X, stylusPoint.Y, stylusPoint.PressureFactor)); - strokeVisual.Redraw(); + + // 实时笔锋:在绘制过程中更新压感并整笔重绘预览;否则预览层固定线宽,收笔后改点集也看不到笔锋变化。 + var committedStroke = strokeVisual.Stroke; + if (committedStroke != null + && Settings.Canvas.InkStyle == 3 + && penType == 0 + && committedStroke.DrawingAttributes != null + && !committedStroke.DrawingAttributes.IsHighlighter + && committedStroke.StylusPoints.Count >= 3) + { + ApplyVelocityBrushTipFromSpeed(committedStroke); + strokeVisual.ForceRedraw(); + } + else + { + strokeVisual.Redraw(); + } } catch (Exception ex) { System.Diagnostics.Debug.WriteLine(ex); } } diff --git a/Ink Canvas/Resources/Settings.cs b/Ink Canvas/Resources/Settings.cs index 88eef9c6..303d5102 100644 --- a/Ink Canvas/Resources/Settings.cs +++ b/Ink Canvas/Resources/Settings.cs @@ -147,7 +147,7 @@ namespace Ink_Canvas [JsonProperty("eraserAutoSwitchBackDelaySeconds")] public int EraserAutoSwitchBackDelaySeconds { get; set; } = 10; // 默认10秒 [JsonProperty("velocityBrushTipMix")] - public double VelocityBrushTipMix { get; set; } = 0.22; + public double VelocityBrushTipMix { get; set; } = 0.45; [JsonProperty("enableVelocityBrushTip")] public bool EnableVelocityBrushTip { get; set; }