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 = ;
  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 = ;
  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(, , 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(, );
  34. var w1 = this.width + ;
  35.  
  36. for (int i = ; 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 = ; 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 = ;
  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 = ;
  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(, , 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 < )
  32. return;
  33.  
  34. var p1 = new Point(double.NegativeInfinity, double.NegativeInfinity);
  35. var w1 = this.width + ;
  36.  
  37. for (int i = ; i < StylusPoints.Count; i++)
  38. {
  39. var p2 = (Point)this.StylusPoints[i];
  40.  
  41. var vector = p1 - p2;
  42.  
  43. var dx = (p2.X - p1.X) / vector.Length;
  44. var dy = (p2.Y - p1.Y) / vector.Length;
  45.  
  46. var w2 = this.width;
  47. if (w1 - vector.Length > this.width)
  48. w2 = w1 - vector.Length;
  49.  
  50. for (int j = ; j < vector.Length; j++)
  51. {
  52. var x = p2.X;
  53. var y = p2.Y;
  54.  
  55. if (!double.IsInfinity(p1.X) && !double.IsInfinity(p1.Y))
  56. {
  57. x = p1.X + dx;
  58. y = p1.Y + dy;
  59. }
  60.  
  61. drawingContext.DrawImage(imageSource, new Rect(x - w2 / 2.0, y - w2 / 2.0, w2, w2));
  62.  
  63. p1 = new Point(x, y);
  64.  
  65. if (double.IsInfinity(vector.Length))
  66. break;
  67. }
  68. }
  69. }
  70. }
  1. public class ChinesebrushCanvas : InkCanvas
  2. {
  3. public ChinesebrushCanvas()
  4. {
  5. //当然要换上我们特地搞出来的ChinesebrushRenderer
  6. this.DynamicRenderer = new ChinesebrushRenderer();
  7. }
  8.  
  9. protected override void OnStrokeCollected(InkCanvasStrokeCollectedEventArgs e)
  10. {
  11. //感兴趣的童鞋,注释这一句看看?
  12. this.Strokes.Remove(e.Stroke);
  13.  
  14. this.Strokes.Add(new ChinesebrushStroke(e.Stroke.StylusPoints, this.DefaultDrawingAttributes.Color));
  15. }
  16. }

笔画原图:

以上代码只是解决了1、2点,第三点嘛~毕竟每个人都有自己的粗细,大家自行体会~

吐槽一下:

本以为本篇文章能有点小小贡献,于是发布到“首页”,结果也就存活十多分钟,而且园内搜索还搜不到!

其实这个需求很久以前做项目就有提了,但那时候刚出来工作没多久【12年毕业,工作以后自学WPF】,还是一个菜鸟萌新,一篇相关文章都搜索不到啊不到!【手动哭泣】

之后也陆陆续续做了好多类似项目,但一直使用文中第一种方案,效果也能被客户接受。

哎,期待有缘人吧!毕竟WPF用的人还是太少!

也是,本篇文章没得个“抄袭”的罪名算好的了,还胆大包天的贴出原文链接!

最后【手动满地打滚撒泼~】

WPF 毛笔字的更多相关文章

  1. 在WPF中使用依赖注入的方式创建视图

    在WPF中使用依赖注入的方式创建视图 0x00 问题的产生 互联网时代桌面开发真是越来越少了,很多应用都转到了浏览器端和移动智能终端,相应的软件开发上的新技术应用到桌面开发的文章也很少.我之前主要做W ...

  2. MVVM框架从WPF移植到UWP遇到的问题和解决方法

    MVVM框架从WPF移植到UWP遇到的问题和解决方法 0x00 起因 这几天开始学习UWP了,之前有WPF经验,所以总体感觉还可以,看了一些基础概念和主题,写了几个测试程序,突然想起来了前一段时间在W ...

  3. MVVM模式解析和在WPF中的实现(六) 用依赖注入的方式配置ViewModel并注册消息

    MVVM模式解析和在WPF中的实现(六) 用依赖注入的方式配置ViewModel并注册消息 系列目录: MVVM模式解析和在WPF中的实现(一)MVVM模式简介 MVVM模式解析和在WPF中的实现(二 ...

  4. MVVM模式解析和在WPF中的实现(五)View和ViewModel的通信

    MVVM模式解析和在WPF中的实现(五) View和ViewModel的通信 系列目录: MVVM模式解析和在WPF中的实现(一)MVVM模式简介 MVVM模式解析和在WPF中的实现(二)数据绑定 M ...

  5. MVVM设计模式和WPF中的实现(四)事件绑定

    MVVM设计模式和在WPF中的实现(四) 事件绑定 系列目录: MVVM模式解析和在WPF中的实现(一)MVVM模式简介 MVVM模式解析和在WPF中的实现(二)数据绑定 MVVM模式解析和在WPF中 ...

  6. MVVM模式解析和在WPF中的实现(三)命令绑定

    MVVM模式解析和在WPF中的实现(三) 命令绑定 系列目录: MVVM模式解析和在WPF中的实现(一)MVVM模式简介 MVVM模式解析和在WPF中的实现(二)数据绑定 MVVM模式解析和在WPF中 ...

  7. MVVM模式和在WPF中的实现(二)数据绑定

    MVVM模式解析和在WPF中的实现(二) 数据绑定 系列目录: MVVM模式解析和在WPF中的实现(一)MVVM模式简介 MVVM模式解析和在WPF中的实现(二)数据绑定 MVVM模式解析和在WPF中 ...

  8. MVVM模式和在WPF中的实现(一)MVVM模式简介

    MVVM模式解析和在WPF中的实现(一) MVVM模式简介 系列目录: MVVM模式解析和在WPF中的实现(一)MVVM模式简介 MVVM模式解析和在WPF中的实现(二)数据绑定 MVVM模式解析和在 ...

  9. 逆天通用水印支持Winform,WPF,Web,WP,Win10。支持位置选择(9个位置 ==》[X])

    常用技能:http://www.cnblogs.com/dunitian/p/4822808.html#skill 逆天博客:http://dnt.dkil.net 逆天通用水印扩展篇~新增剪贴板系列 ...

随机推荐

  1. cocos2d-x 游戏开发之有限状态机(FSM) (四)

    cocos2d-x 游戏开发之有限状态机(FSM) (四) 虽然我们了解了FSM,并且可以写自己的FSM,但是有更好的工具帮我们完成这个繁琐的工作.SMC(http://smc.sourceforge ...

  2. 求剁手的分享,如何简单开发js图表

    前段时间做的一个项目里需要用到js图表,在网上找了下,大概找到了highcharts.fusioncharts这些国外产品. 因为都收费,虽然有盗版,我也不敢用,万一被找上们来就砸锅卖铁了要.自己写j ...

  3. ITU-T Technical Paper: QoS的构建模块与机制

    本文翻译自ITU-T的Technical Paper:<How to increase QoS/QoE of IP-based platform(s) to regionally agreed ...

  4. 自动生成材质Material(Unity3D开发之十九)

    猴子原创,欢迎转载.转载请注明: 转载自Cocos2Der-CSDN,谢谢! 原文地址: http://blog.csdn.net/cocos2der/article/details/46854411 ...

  5. RTMPdump(libRTMP) 源代码分析 6: 建立一个流媒体连接 (NetStream部分 1)

    ===================================================== RTMPdump(libRTMP) 源代码分析系列文章: RTMPdump 源代码分析 1: ...

  6. JAVA堆栈的区别

    1. 栈(stack)与堆(heap)都是Java用来在Ram中存放数据的地方.与C++不同,Java自动管理栈和堆,程序员不能直接地设置栈或堆.              2. 栈的优势是,存取速度 ...

  7. 在MinGW下编译ffmpeg

    因为需要使用ffmpeg的相关库和执行文件,所以需要编译最新的ffmpeg代码.为了能在编译成Windows native执行程序(需要在.net中调用该执行程序),这里我们使用MinGW. 1,安装 ...

  8. python---购物车

    购物车功能如下: 1. 输入收入多少,购买商品 2. 打印购物清单,根据清单选择商品: 3. 结算,打印购物清单及总金额 # -*- coding:utf-8 -*- # LC goods=[[1,' ...

  9. A million requests per second with Python

    https://medium.freecodecamp.com/million-requests-per-second-with-Python-95c137af319 Is it possible t ...

  10. Django Push 的一些资料

    先来中文的: 主要讲Orbited: http://sunsetsunrising.com/2009/django_comet.html#gsc.tab=0 再来英文的: http://www.rkb ...