一、先來看看InkCanvas的通常用法:canvas
<InkCanvas>
<InkCanvas.DefaultDrawingAttributes>
<DrawingAttributes StylusTip="Ellipse" Height="8" Width="4" IgnorePressure="False" FitToCurve="True" >
<!--稍微變換一下,就算設備不支持「壓感」,效果也是棒棒-->ide
<DrawingAttributes.StylusTipTransform>
<Matrix M11="1" M12="1.5" M21="2.2" M22="1"/>
</DrawingAttributes.StylusTipTransform>
</DrawingAttributes>
</InkCanvas.DefaultDrawingAttributes>
</InkCanvas>函數
二、自定義InkCanvas,實現毛筆效果性能
找到2篇文章,代碼基本一致,也不知道哪位是原做者抑或都不是原做者?this
使用WPF的自定義InkCanvas實現毛筆效果 【我的以爲該做者爲原創?】spa
wpf inkcanvas customink 毛筆效果 【這位童鞋的話,後面都叛變去搞Unity3D了!】.net
以上代碼缺點:線程
2-一、卡頓【雖然提到了解決辦法,但都沒有給出具體代碼】code
2-二、顏色【毛筆配黑墨纔是正途,但世界是多姿多彩的不是?】orm
2-三、粗細【這個嘛~】
廢話很少說,奉上改進後的代碼:
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 }
筆畫原圖:
以上代碼只是解決了一、2點,第三點嘛~畢竟每一個人都有本身的粗細,你們自行體會~
吐槽一下:
本覺得本篇文章能有點小小貢獻,因而發佈到「首頁」,結果也就存活十多分鐘,並且園內搜索還搜不到!
其實這個需求好久之前作項目就有提了,但那時候剛出來工做沒多久【12年畢業,工做之後自學WPF】,仍是一個菜鳥萌新,一篇相關文章都搜索不到啊不到!【手動哭泣】
以後也陸陸續續作了好多相似項目,但一直使用文中第一種方案,效果也能被客戶接受。
哎,期待有緣人吧!畢竟WPF用的人仍是太少!
也是,本篇文章沒得個「抄襲」的罪名算好的了,還膽大包天的貼出原文連接!
最後【手動滿地打滾撒潑~】