[update] IccInkCanvas 实时笔锋优化

This commit is contained in:
Dubi906w 2024-09-22 16:28:59 +08:00
parent ab24d8240e
commit 856762f555
7 changed files with 302 additions and 63 deletions

View File

@ -10,11 +10,23 @@
<Grid> <Grid>
<StackPanel Orientation="Vertical" Margin="8"> <StackPanel Orientation="Vertical" Margin="8">
<TextBlock FontSize="24">IccInkCanvasDemo</TextBlock> <TextBlock FontSize="24">IccInkCanvasDemo</TextBlock>
<iccInkCanvas:IccBoard Name="IccBoard" Width="512" Height="512"/> <iccInkCanvas:IccBoard EditingMode="None" Name="IccBoard" Width="512" Height="512"/>
<WrapPanel Orientation="Horizontal"> <WrapPanel Orientation="Horizontal">
<Button Content="EditingMode = None" Click="EditingModeChangeToNone_ButtonClick"/> <Button Content="EditingMode = None" Click="EditingModeChangeToNone_ButtonClick"/>
<Button Content="EditingMode = Writing" Click="EditingModeChangeToWriting_ButtonClick"/> <Button Content="EditingMode = Writing" Click="EditingModeChangeToWriting_ButtonClick"/>
</WrapPanel> </WrapPanel>
<TextBlock Name="BoardSettingsTextBlock"/>
<WrapPanel Orientation="Horizontal">
<Button Content="BoardSettings.NibSize = 2" Click="BoardSettingsNibSizeChangeTo2_ButtonClick"/>
<Button Content="BoardSettings.NibSize = 4" Click="BoardSettingsNibSizeChangeTo4_ButtonClick"/>
<Button Content="BoardSettings.NibSize = 6" Click="BoardSettingsNibSizeChangeTo6_ButtonClick"/>
<Button Content="BoardSettings.NibColor = Red" Click="BoardSettingsNibColorChangeToRed_ButtonClick"/>
<Button Content="BoardSettings.NibColor = Blue" Click="BoardSettingsNibColorChangeToBlue_ButtonClick"/>
<Button Content="BoardSettings.NibColor = Green" Click="BoardSettingsNibColorChangeToGreen_ButtonClick"/>
<Button Content="BoardSettings.NibColor = Black" Click="BoardSettingsNibColorChangeToBlack_ButtonClick"/>
<Button Content="BoardSettings.StrokeNibStyle = Solid" Click="BoardSettingsStrokeNibStyleChangeToSolid_ButtonClick"/>
<Button Content="BoardSettings.StrokeNibStyle = Beautiful" Click="BoardSettingsStrokeNibStyleChangeToBeautiful_ButtonClick"/>
</WrapPanel>
</StackPanel> </StackPanel>
</Grid> </Grid>
</Window> </Window>

View File

@ -22,13 +22,56 @@ namespace InkCanvasForClass.IccInkCanvas.Demo {
public partial class MainWindow : Window { public partial class MainWindow : Window {
public MainWindow() { public MainWindow() {
InitializeComponent(); InitializeComponent();
RegisterEventsForBoardSettings();
UpdateBoardSettingsTextBlock();
}
private void UpdateBoardSettingsTextBlock() {
BoardSettingsTextBlock.Text =
$@"Width:{IccBoard.BoardSettings.NibWidth} Height:{IccBoard.BoardSettings.NibHeight} Color:{IccBoard.BoardSettings.NibColor.ToString()}";
}
private void RegisterEventsForBoardSettings() {
IccBoard.BoardSettings.NibWidthChanged += (s, e) => UpdateBoardSettingsTextBlock();
IccBoard.BoardSettings.NibHeightChanged += (s, e) => UpdateBoardSettingsTextBlock();
IccBoard.BoardSettings.NibColorChanged += (s, e) => UpdateBoardSettingsTextBlock();
} }
private void EditingModeChangeToNone_ButtonClick(object sender, RoutedEventArgs e) { private void EditingModeChangeToNone_ButtonClick(object sender, RoutedEventArgs e) {
IccBoard.EditingMode = EditingMode.None; IccBoard.EditingMode = EditingMode.NoneWithHitTest;
} }
private void EditingModeChangeToWriting_ButtonClick(object sender, RoutedEventArgs e) { private void EditingModeChangeToWriting_ButtonClick(object sender, RoutedEventArgs e) {
IccBoard.EditingMode = EditingMode.Writing; IccBoard.EditingMode = EditingMode.Writing;
} }
private void BoardSettingsNibSizeChangeTo2_ButtonClick(object sender, RoutedEventArgs e) {
IccBoard.BoardSettings.NibWidth = 2;
IccBoard.BoardSettings.NibHeight = 2;
}
private void BoardSettingsNibSizeChangeTo4_ButtonClick(object sender, RoutedEventArgs e) {
IccBoard.BoardSettings.NibWidth = 4;
IccBoard.BoardSettings.NibHeight = 4;
}
private void BoardSettingsNibSizeChangeTo6_ButtonClick(object sender, RoutedEventArgs e) {
IccBoard.BoardSettings.NibWidth = 6;
IccBoard.BoardSettings.NibHeight = 6;
}
private void BoardSettingsNibColorChangeToRed_ButtonClick(object sender, RoutedEventArgs e) {
IccBoard.BoardSettings.NibColor = Color.FromRgb(220, 38, 38);
}
private void BoardSettingsNibColorChangeToBlue_ButtonClick(object sender, RoutedEventArgs e) {
IccBoard.BoardSettings.NibColor = Color.FromRgb(37, 99, 235);
}
private void BoardSettingsNibColorChangeToGreen_ButtonClick(object sender, RoutedEventArgs e) {
IccBoard.BoardSettings.NibColor = Color.FromRgb(34, 197, 94);
}
private void BoardSettingsNibColorChangeToBlack_ButtonClick(object sender, RoutedEventArgs e) {
IccBoard.BoardSettings.NibColor = Colors.Black;
}
private void BoardSettingsStrokeNibStyleChangeToSolid_ButtonClick(object sender, RoutedEventArgs e) {
IccBoard.BoardSettings.StrokeNibStyle = StrokeNibStyle.Solid;
}
private void BoardSettingsStrokeNibStyleChangeToBeautiful_ButtonClick(object sender, RoutedEventArgs e) {
IccBoard.BoardSettings.StrokeNibStyle = StrokeNibStyle.Beautiful;
}
} }
} }

View File

@ -10,7 +10,7 @@
<Canvas Width="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorLevel=1, AncestorType={x:Type UserControl}}, Path=ActualWidth}" <Canvas Width="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorLevel=1, AncestorType={x:Type UserControl}}, Path=ActualWidth}"
Height="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorLevel=1, AncestorType={x:Type UserControl}}, Path=ActualHeight}" Height="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorLevel=1, AncestorType={x:Type UserControl}}, Path=ActualHeight}"
ClipToBounds="True" x:Name="InkCanvasHostCanvas" x:FieldModifier="private"> ClipToBounds="True" x:Name="InkCanvasHostCanvas" x:FieldModifier="private">
<local:IccInkCanvas x:Name="InkCanvas" x:FieldModifier="private" Loaded="IccInkCanvas_Loaded"/> <local:IccInkCanvas x:Name="InkCanvas" x:FieldModifier="private" Loaded="IccInkCanvas_Loaded"/>
</Canvas> </Canvas>
</Grid> </Grid>
</UserControl> </UserControl>

View File

@ -96,6 +96,16 @@ namespace InkCanvasForClass.IccInkCanvas {
#endregion #endregion
#region BoardSettings
private void RegisterEventsForBoardSettings() {
BoardSettings.NibWidthChanged += (s,e)=>InkCanvas.DefaultDrawingAttributes.Width = BoardSettings.NibWidth;
BoardSettings.NibHeightChanged += (s,e)=>InkCanvas.DefaultDrawingAttributes.Height = BoardSettings.NibHeight;
BoardSettings.NibColorChanged += (s,e)=>InkCanvas.DefaultDrawingAttributes.Color = BoardSettings.NibColor;
}
#endregion
public IccBoard() { public IccBoard() {
InitializeComponent(); InitializeComponent();
} }
@ -123,6 +133,9 @@ namespace InkCanvasForClass.IccInkCanvas {
ic.DefaultDrawingAttributes.Color = BoardSettings.NibColor; ic.DefaultDrawingAttributes.Color = BoardSettings.NibColor;
ic.BoardSettings = BoardSettings; ic.BoardSettings = BoardSettings;
// BoardSettings 事件注册
RegisterEventsForBoardSettings();
} }
} }

View File

@ -17,12 +17,21 @@ using InkCanvasForClass.IccInkCanvas.Settings;
namespace InkCanvasForClass.IccInkCanvas { namespace InkCanvasForClass.IccInkCanvas {
class CustomDynamicRenderer : DynamicRenderer { class CustomDynamicRenderer : DynamicRenderer {
private class StylusProcessedCallbackData {
public int StylusDeviceID;
public int TabletDeviceID;
}
private Point prevPoint; private Point prevPoint;
private IccInkCanvas _inkCanvas; private IccInkCanvas _inkCanvas;
private List<StylusPoint> pointsList = new List<StylusPoint>(); private List<StylusPoint> pointsList = new List<StylusPoint>();
private StylusProcessedCallbackData _stylusProcessedCallbackData;
public InputtingDeviceType InputType = InputtingDeviceType.None;
private void ClearPointsList() { private void ClearPointsList() {
pointsList.Clear(); pointsList.Clear();
} }
@ -41,14 +50,38 @@ namespace InkCanvasForClass.IccInkCanvas {
protected override void OnStylusDown(RawStylusInput rawStylusInput) { protected override void OnStylusDown(RawStylusInput rawStylusInput) {
prevPoint = new Point(double.NegativeInfinity, double.NegativeInfinity); prevPoint = new Point(double.NegativeInfinity, double.NegativeInfinity);
ClearPointsList(); ClearPointsList();
// 判断当前设备类型
var styDevice = rawStylusInput.StylusDeviceId;
var tabletDevice = rawStylusInput.TabletDeviceId;
var pts = rawStylusInput.GetStylusPoints(); var pts = rawStylusInput.GetStylusPoints();
if (pts.Count == 1) { if (pts.Count == 1) {
PushPoint(pts.Single()); PushPoint(pts.Single());
} else if (pts.Count > 1) { } else if (pts.Count > 1) {
PushPoint(pts[pts.Count-1]); PushPoint(pts[pts.Count-1]);
} }
Trace.WriteLine(pointsList.Count);
base.OnStylusDown(rawStylusInput); base.OnStylusDown(rawStylusInput);
_stylusProcessedCallbackData = new StylusProcessedCallbackData() {
StylusDeviceID = styDevice,
TabletDeviceID = tabletDevice,
};
}
protected override void OnStylusDownProcessed(object callbackData, bool targetVerified) {
var data = _stylusProcessedCallbackData;
var tabletDevices = Tablet.TabletDevices;
if (data.StylusDeviceID == 0 && data.TabletDeviceID == 0) InputType = InputtingDeviceType.Mouse;
foreach (TabletDevice tabletDevice in tabletDevices) {
if (tabletDevice.Id == data.TabletDeviceID && tabletDevice.Type == TabletDeviceType.Stylus)
InputType = InputtingDeviceType.Stylus;
if (tabletDevice.Id == data.TabletDeviceID && tabletDevice.Type == TabletDeviceType.Touch)
InputType = InputtingDeviceType.Touch;
}
base.OnStylusDownProcessed(callbackData, targetVerified);
} }
protected override void OnStylusMove(RawStylusInput rawStylusInput) { protected override void OnStylusMove(RawStylusInput rawStylusInput) {
@ -57,56 +90,107 @@ namespace InkCanvasForClass.IccInkCanvas {
if (pts.Count == 1) { if (pts.Count == 1) {
PushPoint(pts.Single()); PushPoint(pts.Single());
} else if (pts.Count > 1) { } else if (pts.Count > 1) {
PushPoint(pts[pts.Count-1]); if (InputType != InputtingDeviceType.Stylus) PushPoint(pts[pts.Count-1]);
} }
Trace.WriteLine(pointsList.Count); }
protected override void OnStylusUpProcessed(object callbackData, bool targetVerified) {
//InputType = InputtingDeviceType.None;
// TODO: 触摸支持
base.OnStylusUpProcessed(callbackData, targetVerified);
}
private void DrawBeautifulNibStroke(DrawingContext drawingContext) {
try {
var sp = new StylusPointCollection();
var n = pointsList.Count - 1;
var pressure = 0.1;
var x = 10;
if (n == 1) return;
if (n >= x) {
for (var i = 0; i < n - x; i++) {
var point = new StylusPoint();
point.PressureFactor = (float)0.5;
point.X = pointsList[i].X;
point.Y = pointsList[i].Y;
sp.Add(point);
}
for (var i = n - x; i <= n; i++) {
var point = new StylusPoint();
point.PressureFactor = (float)((0.5 - pressure) * (n - i) / x + pressure);
point.X = pointsList[i].X;
point.Y = pointsList[i].Y;
sp.Add(point);
}
} else {
for (var i = 0; i <= n; i++) {
var point = new StylusPoint();
point.PressureFactor = (float)(0.4 * (n - i) / n + pressure);
point.X = pointsList[i].X;
point.Y = pointsList[i].Y;
sp.Add(point);
}
}
var da = DrawingAttributes.Clone();
da.Width -= 0.5;
da.Height -= 0.5;
var stk = new Stroke(sp, da);
stk.Draw(drawingContext);
} catch {}
}
private void DrawSolidNibStroke(DrawingContext drawingContext) {
try {
var sp = new StylusPointCollection();
foreach (var pt in pointsList) {
var point = new StylusPoint();
point.PressureFactor = (float)0.5;
point.X = pt.X;
point.Y = pt.Y;
sp.Add(point);
}
var da = DrawingAttributes.Clone();
da.Width -= 0.5;
da.Height -= 0.5;
var stk = new Stroke(sp, da);
stk.Draw(drawingContext);
}
catch {}
}
private void DrawSolidNibStrokeForStylus(StylusPointCollection stylusPoints, DrawingContext drawingContext) {
try {
var sp = new StylusPointCollection();
foreach (var pt in stylusPoints) {
var point = new StylusPoint();
point.PressureFactor = (float)0.5;
point.X = pt.X;
point.Y = pt.Y;
sp.Add(point);
}
var da = DrawingAttributes.Clone();
var stk = new Stroke(sp, da);
stk.Draw(drawingContext);
}
catch {}
} }
protected override void OnDraw(DrawingContext drawingContext, protected override void OnDraw(DrawingContext drawingContext,
StylusPointCollection stylusPoints, StylusPointCollection stylusPoints,
Geometry geometry, Brush fillBrush) { Geometry geometry, Brush fillBrush) {
if (_inkCanvas.BoardSettings.StrokeNibStyle == StrokeNibStyle.Beautiful) { if (_inkCanvas.BoardSettings.StrokeNibStyle == StrokeNibStyle.Beautiful && InputType != InputtingDeviceType.Stylus) {
try { DrawBeautifulNibStroke(drawingContext);
var sp = new StylusPointCollection(); } else if (_inkCanvas.BoardSettings.StrokeNibStyle == StrokeNibStyle.Solid && InputType != InputtingDeviceType.Stylus) {
var n = pointsList.Count - 1; DrawSolidNibStroke(drawingContext);
var pressure = 0.1; } else if (_inkCanvas.BoardSettings.StrokeNibStyle == StrokeNibStyle.Solid && InputType == InputtingDeviceType.Stylus) {
var x = 10; DrawSolidNibStrokeForStylus(stylusPoints, drawingContext);
if (n == 1) return; } else if (_inkCanvas.BoardSettings.StrokeNibStyle == StrokeNibStyle.Default) {
if (n >= x) { base.OnDraw(drawingContext, stylusPoints, geometry, fillBrush);
for (var i = 0; i < n - x; i++) {
var point = new StylusPoint();
point.PressureFactor = (float)0.5;
point.X = pointsList[i].X;
point.Y = pointsList[i].Y;
sp.Add(point);
}
for (var i = n - x; i <= n; i++) {
var point = new StylusPoint();
point.PressureFactor = (float)((0.5 - pressure) * (n - i) / x + pressure);
point.X = pointsList[i].X;
point.Y = pointsList[i].Y;
sp.Add(point);
}
} else {
for (var i = 0; i <= n; i++) {
var point = new StylusPoint();
point.PressureFactor = (float)(0.4 * (n - i) / n + pressure);
point.X = pointsList[i].X;
point.Y = pointsList[i].Y;
sp.Add(point);
}
}
var da = DrawingAttributes.Clone();
da.Width -= 0.5;
da.Height -= 0.5;
var stk = new Stroke(sp, da);
stk.Draw(drawingContext);
} catch {}
} else { } else {
base.OnDraw(drawingContext, stylusPoints, geometry, fillBrush); base.OnDraw(drawingContext, stylusPoints, geometry, fillBrush);
} }
@ -160,7 +244,7 @@ namespace InkCanvasForClass.IccInkCanvas {
this.Strokes.Add(customStroke); this.Strokes.Add(customStroke);
} }
if (BoardSettings.StrokeNibStyle == StrokeNibStyle.Beautiful) { if (BoardSettings.StrokeNibStyle == StrokeNibStyle.Beautiful && customDynamicRenderer.InputType != InputtingDeviceType.Stylus) {
try { try {
var stylusPoints = new StylusPointCollection(); var stylusPoints = new StylusPointCollection();
var n = customStroke.StylusPoints.Count - 1; var n = customStroke.StylusPoints.Count - 1;
@ -199,6 +283,18 @@ namespace InkCanvasForClass.IccInkCanvas {
customStroke.StylusPoints = stylusPoints; customStroke.StylusPoints = stylusPoints;
} catch { } } catch { }
} else if (BoardSettings.StrokeNibStyle == StrokeNibStyle.Solid) {
try {
var sp = new StylusPointCollection();
foreach (var pt in customStroke.StylusPoints) {
var point = new StylusPoint();
point.PressureFactor = (float)0.5;
point.X = pt.X;
point.Y = pt.Y;
sp.Add(point);
}
customStroke.StylusPoints = sp;
} catch { }
} }
InkCanvasStrokeCollectedEventArgs args = InkCanvasStrokeCollectedEventArgs args =
@ -207,5 +303,7 @@ namespace InkCanvasForClass.IccInkCanvas {
} }
public event EventHandler<RoutedEventArgs> DeleteKeyCommandFired; public event EventHandler<RoutedEventArgs> DeleteKeyCommandFired;
public InputtingDeviceType InputtingDeviceType { get; private set; }
} }
} }

View File

@ -9,34 +9,103 @@ namespace InkCanvasForClass.IccInkCanvas.Settings {
public class BoardSettings { public class BoardSettings {
public BoardSettings() {} public BoardSettings() {}
private double _NibWidth { get; set; } = 4.00;
/// <summary> /// <summary>
/// 笔尖长度 /// 笔尖长度
/// </summary> /// </summary>
public double NibWidth { get; set; } = 4.00; public double NibWidth {
get => _NibWidth;
set {
if (Math.Abs(_NibWidth - value) < 0.0001) return;
_NibWidth = value;
NibWidthChanged?.Invoke(this,EventArgs.Empty);
}
}
private double _NibHeight { get; set; } = 4.00;
/// <summary> /// <summary>
/// 笔尖高度 /// 笔尖高度
/// </summary> /// </summary>
public double NibHeight { get; set; } = 4.00; public double NibHeight {
get => _NibWidth;
/// <summary> set {
/// 笔尖大小,适合笔尖类型为普通笔时使用 if (Math.Abs(_NibHeight - value) < 0.0001) return;
/// </summary> _NibHeight = value;
public double NibSize { NibHeightChanged?.Invoke(this,EventArgs.Empty);
get => (NibWidth + NibHeight) / 2; }
set => NibWidth = NibHeight = value;
} }
public NibType NibType { get; set; } = NibType.Default; private NibType _NibType { get; set; } = NibType.Default;
public NibType NibType {
get => _NibType;
set {
if (_NibType == value) return;
_NibType = value;
NibTypeChanged?.Invoke(this,EventArgs.Empty);
}
}
private Color _NibColor { get; set; } = Colors.Black;
/// <summary> /// <summary>
/// 笔尖颜色 /// 笔尖颜色
/// </summary> /// </summary>
public Color NibColor { get; set; } = Colors.Black; public Color NibColor {
get => _NibColor;
set {
if (_NibColor.Equals(value)) return;
_NibColor = value;
NibColorChanged?.Invoke(this,EventArgs.Empty);
}
}
private StrokeNibStyle _StrokeNibStyle { get; set; } = StrokeNibStyle.Beautiful;
/// <summary> /// <summary>
/// 笔锋样式类型,默认有笔锋 /// 笔锋样式类型,默认有笔锋
/// </summary> /// </summary>
public StrokeNibStyle StrokeNibStyle { get; set; } = StrokeNibStyle.Beautiful; public StrokeNibStyle StrokeNibStyle {
get => _StrokeNibStyle;
set {
if (_StrokeNibStyle == value) return;
_StrokeNibStyle = value;
StrokeNibStyleChanged?.Invoke(this,EventArgs.Empty);
}
}
private bool _IsForceIgnoreStylusPressure { get; set; } = false;
/// <summary>
/// 强制忽略支持压力传感的输入设备返回的真实压力值(比如支持压感的手写笔)
/// </summary>
public bool IsForceIgnoreStylusPressure {
get => _IsForceIgnoreStylusPressure;
set {
if (_IsForceIgnoreStylusPressure == value) return;
_IsForceIgnoreStylusPressure = value;
IsForceIgnoreStylusPressureChanged?.Invoke(this,EventArgs.Empty);
}
}
#region Events
public event EventHandler<EventArgs> NibWidthChanged;
public event EventHandler<EventArgs> NibHeightChanged;
public event EventHandler<EventArgs> NibColorChanged;
public event EventHandler<EventArgs> NibTypeChanged;
public event EventHandler<EventArgs> StrokeNibStyleChanged;
public event EventHandler<EventArgs> IsForceIgnoreStylusPressureChanged;
#endregion
}
public enum InputtingDeviceType {
None,
Mouse,
Touch,
Stylus
} }
} }

View File

@ -25,11 +25,15 @@ namespace InkCanvasForClass.IccInkCanvas.Settings {
/// </summary> /// </summary>
public enum StrokeNibStyle { public enum StrokeNibStyle {
/// <summary> /// <summary>
/// 无笔锋 /// 默认,直接根据输入设备提供的压力值正常渲染
/// </summary>
Default,
/// <summary>
/// 强制无笔锋
/// </summary> /// </summary>
Solid, Solid,
/// <summary> /// <summary>
/// 有笔锋,基于固定点集算法计算 /// 有笔锋,基于固定点集算法计算,触笔设备不套用算法
/// </summary> /// </summary>
Beautiful Beautiful
} }