1、先来看看InkCanvas的一般用法:
<InkCanvas>
<InkCanvas.DefaultDrawingAttributes>
<DrawingAttributes StylusTip="Ellipse" Height="8" Width="4" IgnorePressure="False" FitToCurve="True" >
<!--稍微变换一下,就算设备不支持“压感”,效果也是棒棒--> <DrawingAttributes.StylusTipTransform>
<Matrix M11="1" M12="1.5" M21="2.2" M22="1"/>
</DrawingAttributes.StylusTipTransform>
</DrawingAttributes>
</InkCanvas.DefaultDrawingAttributes>
</InkCanvas>
2、自定义InkCanvas,实现毛笔效果
找到2篇文章,代码基本一致,也不知道哪位是原作者抑或都不是原作者?
使用WPF的自定义InkCanvas实现毛笔效果 【个人觉得该作者为原创?】
wpf inkcanvas customink 毛笔效果 【这位童鞋的话,后面都叛变去搞Unity3D了!】
以上代码缺点:
2-1、卡顿【虽然提到了解决办法,但都没有给出具体代码】
2-2、颜色【毛笔配黑墨才是正途,但世界是多姿多彩的不是?】
2-3、粗细【这个嘛~】
废话不多说,奉上改进后的代码:
1 public class ChinesebrushRenderer : DynamicRenderer
2 {
3 private ImageSource imageSource;
4 private readonly double width = 16;
5
6 protected override void OnDrawingAttributesReplaced()
7 {
8 if (DesignerProperties.GetIsInDesignMode(this.Element))
9 return;
10
11 base.OnDrawingAttributesReplaced();
12
13 var dv = new DrawingVisual();
14 var size = 90;
15 using (var conext = dv.RenderOpen())
16 {
17 //[关键]OpacityMask了解下?也许有童鞋想到的办法是,各种颜色的图片来一张?
18 conext.PushOpacityMask(new ImageBrush(new BitmapImage(new Uri(AppDomain.CurrentDomain.BaseDirectory + "Images\\pen.png", UriKind.Absolute))));
19 //用颜色生成画笔画一个矩形
20 conext.DrawRectangle(new SolidColorBrush(this.DrawingAttributes.Color), null, new Rect(0, 0, size, size));
21 conext.Close();
22 }
23 var rtb = new RenderTargetBitmap(size, size, 96d, 96d, PixelFormats.Pbgra32);
24 rtb.Render(dv);
25 imageSource = BitmapFrame.Create(rtb);
26 //[重要]此乃解决卡顿问题的关键!
27 imageSource.Freeze();
28 }
29
30 protected override void OnDraw(DrawingContext drawingContext, StylusPointCollection stylusPoints, Geometry geometry, Brush fillBrush)
31 {
32 var p1 = new Point(double.NegativeInfinity, double.NegativeInfinity);
33 var p2 = new Point(0, 0);
34 var w1 = this.width + 20;
35
36 for (int i = 0; i < stylusPoints.Count; i++)
37 {
38 p2 = (Point)stylusPoints[i];
39
40 //两点相减得到一个向量[高中数学知识了解下?]
41 var vector = p1 - p2;
42
43 //得到 x、y的变化值
44 var dx = (p2.X - p1.X) / vector.Length;
45 var dy = (p2.Y - p1.Y) / vector.Length;
46
47 var w2 = this.width;
48 if (w1 - vector.Length > this.width)
49 w2 = w1 - vector.Length;
50
51 //为啥又来一个for?图像重叠,实现笔画的连续性,感兴趣的童鞋可以把for取消掉看看效果
52 for (int j = 0; j < vector.Length; j++)
53 {
54 var x = p2.X;
55 var y = p2.Y;
56
57 if (!double.IsInfinity(p1.X) && !double.IsInfinity(p1.Y))
58 {
59 x = p1.X + dx;
60 y = p1.Y + dy;
61 }
62
63 //画图,没啥可说的
64 drawingContext.DrawImage(imageSource, new Rect(x - w2 / 2.0, y - w2 / 2.0, w2, w2));
65
66 //再把新的坐标赋值给p1,以序后来
67 p1 = new Point(x, y);
68
69 if (double.IsInfinity(vector.Length))
70 break;
71
72 }
73 }
74 }
1 public class ChinesebrushStroke : Stroke
2 {
3
4 private ImageSource imageSource;
5 private readonly double width = 16;
6
7 public ChinesebrushStroke(StylusPointCollection stylusPointCollection, Color color) : base(stylusPointCollection)
8 {
9 if (DesignerProperties.GetIsInDesignMode(App.Current.MainWindow))
10 return;
11 var dv = new DrawingVisual();
12 var size = 90;
13 using (var conext = dv.RenderOpen())
14 {
15 conext.PushOpacityMask(new ImageBrush(new BitmapImage(new Uri(AppDomain.CurrentDomain.BaseDirectory + "Images\\pen.png", UriKind.Absolute))));
16 conext.DrawRectangle(new SolidColorBrush(color), null, new Rect(0, 0, size, size));
17 conext.Close();
18 }
19 var rtb = new RenderTargetBitmap(size, size, 96d, 96d, PixelFormats.Pbgra32);
20 rtb.Render(dv);
21 imageSource = BitmapFrame.Create(rtb);
22
23 //Freezable 类提供特殊功能,以便在使用修改或复制开销很大的对象时帮助提高应用程序性能
24 //WPF中的Frozen(冻结)与线程及其他相关问题
25 imageSource.Freeze();
26 }
27
28 //卡顿就是该函数造成,每写完一笔就会调用,当笔画过长,后果可想而知~
29 protected override void DrawCore(DrawingContext drawingContext, DrawingAttributes drawingAttributes)
30 {
31 if (this.StylusPoints?.Count < 1)
32 return;
33
34 var p1 = new Point(double.NegativeInfinity, double.NegativeInfinity);
35 var w1 = this.width + 20;
36
37
38 for (int i = 0; i < StylusPoints.Count; i++)
39 {
40 var p2 = (Point)this.StylusPoints[i];
41
42 var vector = p1 - p2;
43
44 var dx = (p2.X - p1.X) / vector.Length;
45 var dy = (p2.Y - p1.Y) / vector.Length;
46
47 var w2 = this.width;
48 if (w1 - vector.Length > this.width)
49 w2 = w1 - vector.Length;
50
51 for (int j = 0; j < vector.Length; j++)
52 {
53 var x = p2.X;
54 var y = p2.Y;
55
56 if (!double.IsInfinity(p1.X) && !double.IsInfinity(p1.Y))
57 {
58 x = p1.X + dx;
59 y = p1.Y + dy;
60 }
61
62 drawingContext.DrawImage(imageSource, new Rect(x - w2 / 2.0, y - w2 / 2.0, w2, w2));
63
64 p1 = new Point(x, y);
65
66 if (double.IsInfinity(vector.Length))
67 break;
68 }
69 }
70 }
71 }
1 public class ChinesebrushCanvas : InkCanvas
2 {
3 public ChinesebrushCanvas()
4 {
5 //当然要换上我们特地搞出来的ChinesebrushRenderer
6 this.DynamicRenderer = new ChinesebrushRenderer();
7 }
8
9
10 protected override void OnStrokeCollected(InkCanvasStrokeCollectedEventArgs e)
11 {
12 //感兴趣的童鞋,注释这一句看看?
13 this.Strokes.Remove(e.Stroke);
14
15 this.Strokes.Add(new ChinesebrushStroke(e.Stroke.StylusPoints, this.DefaultDrawingAttributes.Color));
16 }
17 }
笔画原图:
以上代码只是解决了1、2点,第三点嘛~毕竟每个人都有自己的粗细,大家自行体会~