WPF 毛笔字
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、粗细【这个嘛~】
废话不多说,奉上改进后的代码:
- public class ChinesebrushRenderer : DynamicRenderer
- {
- private ImageSource imageSource;
- private readonly double width = ;
- protected override void OnDrawingAttributesReplaced()
- {
- if (DesignerProperties.GetIsInDesignMode(this.Element))
- return;
- base.OnDrawingAttributesReplaced();
- var dv = new DrawingVisual();
- var size = ;
- using (var conext = dv.RenderOpen())
- {
- //[关键]OpacityMask了解下?也许有童鞋想到的办法是,各种颜色的图片来一张?
- conext.PushOpacityMask(new ImageBrush(new BitmapImage(new Uri(AppDomain.CurrentDomain.BaseDirectory + "Images\\pen.png", UriKind.Absolute))));
- //用颜色生成画笔画一个矩形
- conext.DrawRectangle(new SolidColorBrush(this.DrawingAttributes.Color), null, new Rect(, , size, size));
- conext.Close();
- }
- var rtb = new RenderTargetBitmap(size, size, 96d, 96d, PixelFormats.Pbgra32);
- rtb.Render(dv);
- imageSource = BitmapFrame.Create(rtb);
- //[重要]此乃解决卡顿问题的关键!
- imageSource.Freeze();
- }
- protected override void OnDraw(DrawingContext drawingContext, StylusPointCollection stylusPoints, Geometry geometry, Brush fillBrush)
- {
- var p1 = new Point(double.NegativeInfinity, double.NegativeInfinity);
- var p2 = new Point(, );
- var w1 = this.width + ;
- for (int i = ; i < stylusPoints.Count; i++)
- {
- p2 = (Point)stylusPoints[i];
- //两点相减得到一个向量[高中数学知识了解下?]
- var vector = p1 - p2;
- //得到 x、y的变化值
- var dx = (p2.X - p1.X) / vector.Length;
- var dy = (p2.Y - p1.Y) / vector.Length;
- var w2 = this.width;
- if (w1 - vector.Length > this.width)
- w2 = w1 - vector.Length;
- //为啥又来一个for?图像重叠,实现笔画的连续性,感兴趣的童鞋可以把for取消掉看看效果
- for (int j = ; j < vector.Length; j++)
- {
- var x = p2.X;
- var y = p2.Y;
- if (!double.IsInfinity(p1.X) && !double.IsInfinity(p1.Y))
- {
- x = p1.X + dx;
- y = p1.Y + dy;
- }
- //画图,没啥可说的
- drawingContext.DrawImage(imageSource, new Rect(x - w2 / 2.0, y - w2 / 2.0, w2, w2));
- //再把新的坐标赋值给p1,以序后来
- p1 = new Point(x, y);
- if (double.IsInfinity(vector.Length))
- break;
- }
- }
- }
- public class ChinesebrushStroke : Stroke
- {
- private ImageSource imageSource;
- private readonly double width = ;
- public ChinesebrushStroke(StylusPointCollection stylusPointCollection, Color color) : base(stylusPointCollection)
- {
- if (DesignerProperties.GetIsInDesignMode(App.Current.MainWindow))
- return;
- var dv = new DrawingVisual();
- var size = ;
- using (var conext = dv.RenderOpen())
- {
- conext.PushOpacityMask(new ImageBrush(new BitmapImage(new Uri(AppDomain.CurrentDomain.BaseDirectory + "Images\\pen.png", UriKind.Absolute))));
- conext.DrawRectangle(new SolidColorBrush(color), null, new Rect(, , size, size));
- conext.Close();
- }
- var rtb = new RenderTargetBitmap(size, size, 96d, 96d, PixelFormats.Pbgra32);
- rtb.Render(dv);
- imageSource = BitmapFrame.Create(rtb);
- //Freezable 类提供特殊功能,以便在使用修改或复制开销很大的对象时帮助提高应用程序性能
- //WPF中的Frozen(冻结)与线程及其他相关问题
- imageSource.Freeze();
- }
- //卡顿就是该函数造成,每写完一笔就会调用,当笔画过长,后果可想而知~
- protected override void DrawCore(DrawingContext drawingContext, DrawingAttributes drawingAttributes)
- {
- if (this.StylusPoints?.Count < )
- return;
- var p1 = new Point(double.NegativeInfinity, double.NegativeInfinity);
- var w1 = this.width + ;
- for (int i = ; i < StylusPoints.Count; i++)
- {
- var p2 = (Point)this.StylusPoints[i];
- var vector = p1 - p2;
- var dx = (p2.X - p1.X) / vector.Length;
- var dy = (p2.Y - p1.Y) / vector.Length;
- var w2 = this.width;
- if (w1 - vector.Length > this.width)
- w2 = w1 - vector.Length;
- for (int j = ; j < vector.Length; j++)
- {
- var x = p2.X;
- var y = p2.Y;
- if (!double.IsInfinity(p1.X) && !double.IsInfinity(p1.Y))
- {
- x = p1.X + dx;
- y = p1.Y + dy;
- }
- drawingContext.DrawImage(imageSource, new Rect(x - w2 / 2.0, y - w2 / 2.0, w2, w2));
- p1 = new Point(x, y);
- if (double.IsInfinity(vector.Length))
- break;
- }
- }
- }
- }
- public class ChinesebrushCanvas : InkCanvas
- {
- public ChinesebrushCanvas()
- {
- //当然要换上我们特地搞出来的ChinesebrushRenderer
- this.DynamicRenderer = new ChinesebrushRenderer();
- }
- protected override void OnStrokeCollected(InkCanvasStrokeCollectedEventArgs e)
- {
- //感兴趣的童鞋,注释这一句看看?
- this.Strokes.Remove(e.Stroke);
- this.Strokes.Add(new ChinesebrushStroke(e.Stroke.StylusPoints, this.DefaultDrawingAttributes.Color));
- }
- }
笔画原图:
以上代码只是解决了1、2点,第三点嘛~毕竟每个人都有自己的粗细,大家自行体会~
吐槽一下:
本以为本篇文章能有点小小贡献,于是发布到“首页”,结果也就存活十多分钟,而且园内搜索还搜不到!
其实这个需求很久以前做项目就有提了,但那时候刚出来工作没多久【12年毕业,工作以后自学WPF】,还是一个菜鸟萌新,一篇相关文章都搜索不到啊不到!【手动哭泣】
之后也陆陆续续做了好多类似项目,但一直使用文中第一种方案,效果也能被客户接受。
哎,期待有缘人吧!毕竟WPF用的人还是太少!
也是,本篇文章没得个“抄袭”的罪名算好的了,还胆大包天的贴出原文链接!
最后【手动满地打滚撒泼~】
WPF 毛笔字的更多相关文章
- 在WPF中使用依赖注入的方式创建视图
在WPF中使用依赖注入的方式创建视图 0x00 问题的产生 互联网时代桌面开发真是越来越少了,很多应用都转到了浏览器端和移动智能终端,相应的软件开发上的新技术应用到桌面开发的文章也很少.我之前主要做W ...
- MVVM框架从WPF移植到UWP遇到的问题和解决方法
MVVM框架从WPF移植到UWP遇到的问题和解决方法 0x00 起因 这几天开始学习UWP了,之前有WPF经验,所以总体感觉还可以,看了一些基础概念和主题,写了几个测试程序,突然想起来了前一段时间在W ...
- MVVM模式解析和在WPF中的实现(六) 用依赖注入的方式配置ViewModel并注册消息
MVVM模式解析和在WPF中的实现(六) 用依赖注入的方式配置ViewModel并注册消息 系列目录: MVVM模式解析和在WPF中的实现(一)MVVM模式简介 MVVM模式解析和在WPF中的实现(二 ...
- MVVM模式解析和在WPF中的实现(五)View和ViewModel的通信
MVVM模式解析和在WPF中的实现(五) View和ViewModel的通信 系列目录: MVVM模式解析和在WPF中的实现(一)MVVM模式简介 MVVM模式解析和在WPF中的实现(二)数据绑定 M ...
- MVVM设计模式和WPF中的实现(四)事件绑定
MVVM设计模式和在WPF中的实现(四) 事件绑定 系列目录: MVVM模式解析和在WPF中的实现(一)MVVM模式简介 MVVM模式解析和在WPF中的实现(二)数据绑定 MVVM模式解析和在WPF中 ...
- MVVM模式解析和在WPF中的实现(三)命令绑定
MVVM模式解析和在WPF中的实现(三) 命令绑定 系列目录: MVVM模式解析和在WPF中的实现(一)MVVM模式简介 MVVM模式解析和在WPF中的实现(二)数据绑定 MVVM模式解析和在WPF中 ...
- MVVM模式和在WPF中的实现(二)数据绑定
MVVM模式解析和在WPF中的实现(二) 数据绑定 系列目录: MVVM模式解析和在WPF中的实现(一)MVVM模式简介 MVVM模式解析和在WPF中的实现(二)数据绑定 MVVM模式解析和在WPF中 ...
- MVVM模式和在WPF中的实现(一)MVVM模式简介
MVVM模式解析和在WPF中的实现(一) MVVM模式简介 系列目录: MVVM模式解析和在WPF中的实现(一)MVVM模式简介 MVVM模式解析和在WPF中的实现(二)数据绑定 MVVM模式解析和在 ...
- 逆天通用水印支持Winform,WPF,Web,WP,Win10。支持位置选择(9个位置 ==》[X])
常用技能:http://www.cnblogs.com/dunitian/p/4822808.html#skill 逆天博客:http://dnt.dkil.net 逆天通用水印扩展篇~新增剪贴板系列 ...
随机推荐
- iOS监听模式系列之推送消息通知
推送通知 和本地通知不同,推送通知是由应用服务提供商发起的,通过苹果的APNs(Apple Push Notification Server)发送到应用客户端.下面是苹果官方关于推送通知的过程示意图: ...
- linux打包压缩常用命令
打包: zip gzip bzip2 tar xz //rar zip 包 zip xxx.zip test.c 压缩 unzip xxx.zip ...
- C/C++内存布局及对齐
1.源文件转换为可执行文件 源文件经过以下几步生成可执行文件: 1.预处理(preprocessor):对#include.#define.#ifdef/#endif.#ifndef/#endif等进 ...
- mahout系列----Dirichlet 分布
Dirichlet分布可以看做是分布之上的分布.如何理解这句话,我们可以先举个例子:假设我们有一个骰子,其有六面,分别为{1,2,3,4,5,6}.现在我们做了10000次投掷的实验,得到的实验结果是 ...
- Stripe Compaction
借鉴于LevelDB.Cassandra的Compaction方法,https://issues.apache.org/jira/browse/HBASE-7667 提出了Stripe Compact ...
- OpenCV+OpenCL stereo match 代码
之前配置cuda跟opencv 的混合编程,发现只要使用的东西多半还要用opencv的代码编译一次,加上cuda的编译太浪费时间了,我看了几个博客,觉的opencl这个可能会比较好整,就把opencv ...
- 面试之路(28)-反转链表(reverse ListNode)
反转链表: java类 public class ListNode{ int key; ListNode next; } 思路分析: 需要三个指针,current,prev和next. current ...
- 恶补web之五:dhtml学习
dhtml是一种使html页面具有动态特性的艺术.对于多数人来说dhtml意味着html(html DOM),样式表和javascript的组合. dhtml不是w3c标准.dhtml指动态html, ...
- vicoapp使用备忘
vico是一个模式编辑器,意味着没用过vi之类编辑器的童鞋用起来肯定觉得很不习惯. 模式切换 i:切至编辑模式,在光标前插入 a:切至编辑模式,在在光标后插入 I:类似于i,不过在行首插入 esc键: ...
- ES6之let命令
ES6新增了let命令,用来声明变量.它的用法类似于var. let和var声明变量的区别: 1.let声明的变量,只在let命令所在的代码块内有效,出了这个块级作用域就不起作用 先看一个例子: { ...