WPF InkCanvas 毛筆效果


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點,第三點嘛~畢竟每個人都有自己的粗細,大家自行體會~

 

吐槽一下:

本以為本篇文章能有點小小貢獻,於是發布到“首頁”,結果也就存活十多分鍾,而且園內搜索還搜不到!

其實這個需求很久以前做項目就有提了,但那時候剛出來工作沒多久【12年畢業,工作以后自學WPF】,還是一個菜鳥萌新,一篇相關文章都搜索不到啊不到!【手動哭泣】

之后也陸陸續續做了好多類似項目,但一直使用文中第一種方案,效果也能被客戶接受。

哎,期待有緣人吧!畢竟WPF用的人還是太少!

也是,本篇文章沒得個“抄襲”的罪名算好的了,還膽大包天的貼出原文鏈接!

最后【手動滿地打滾撒潑~】


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM