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、粗细【这个嘛~】
废话不多说,奉上改进后的代码:
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 InkCanvas 毛笔效果的更多相关文章
- WPF提示框效果
WPF提示框效果 1,新建WPF应用程序 2,添加用户控件Message 3,在Message中编写如下代码 <Border x:Name="border" BorderTh ...
- wpf 模拟3D效果(和手机浏览图片效果相似)(附源码)
原文 wpf 模拟3D效果(和手机浏览图片效果相似)(附源码) pf的3D是一个很有意思的东西,类似于ps的效果,类似于电影动画的效果,因为动画的效果,(对于3D基础的摄像机,光源,之类不介绍,对于依 ...
- [WPF] 圆形等待效果
原文:[WPF] 圆形等待效果 自己做着玩儿的,留着以后用,效果类似下面的 GIF 动画. <Grid Width="35" Height="35"> ...
- WPF实现射线效果动画
原文:WPF实现射线效果动画 最近的一个项目中有个需求是:从一个点向其它多个点发出射线,要求这些射线同时发出,同时到达. 我就想到了用WPF的动画来实现.WPF中有Line类用于绘制直线,但这个类中好 ...
- WPF 的毛玻璃效果
原文:WPF 的毛玻璃效果 版权声明:本文为博主原创文章,未经博主允许不得转载. https://blog.csdn.net/koloumi/article/details/76917519 其实很简 ...
- WPF实现抽屉效果
原文:WPF实现抽屉效果 界面代码(xaml): <Window x:Class="TransAnimation.MainWindow" xmlns="http:/ ...
- WPF 实现水纹效果
原文:WPF 实现水纹效果 鼠标滑过产生水纹,效果图如下: XMAL就放置了一个img标签 后台主要代码 窗体加载: private void Window_Loaded(object s ...
- WPF实现选项卡效果(2)——动态添加AvalonDock选项卡
原文:WPF实现选项卡效果(2)--动态添加AvalonDock选项卡 简介 在前面一篇文章里面,我们使用AvalonDock实现了类似于VS的选项卡(或者浏览器的选项卡)效果.但是我们是通过xaml ...
- WPF实现选项卡效果(3)——自定义动态添加的AvalonDock选项卡内容
原文:WPF实现选项卡效果(3)--自定义动态添加的AvalonDock选项卡内容 简介 在前面一篇文章里面,我们实现了AvalonDock选项卡的动态添加,但是对于选项卡里面的内容,我们并没有实现任 ...
随机推荐
- POJ 1745 Divisibility DP
POJ:http://poj.org/problem?id=1745 A完这题去买福鼎肉片,和舍友去买滴~舍友感慨"这一天可以卖好几百份,每份就算赚一块钱..那么一个月..一年...&quo ...
- stm32的pwm波
- pppoe-环境下的mtu和mss
路由器上在宽带拨号高级设置页面会有设置数据包MTU的页面 数据包MTU(字节):1480 (默认是1480,如非必要,请勿修改) PPPoE/ADSL:1492 ,可以尝试修改为1492 MTU: M ...
- markdown + vim
https://www.jianshu.com/p/24aefcd4ca93https://github.com/isnowfy/python-vim-instant-markdownhttps:// ...
- 安装Centos时“sda必须有一个GPT磁盘标签”
http://jingyan.baidu.com/article/c45ad29c272326051753e2d1.html
- php实现Bloom Filter
Bloom Filter(BF) 是由Bloom在1970年提出的一种多哈希函数映射的高速查找算法,用于高速查找某个元素是否属于集合, 但不要求百分百的准确率. Bloom filter通经常使用于爬 ...
- 解决Eclipse中文乱码 分类: B1_JAVA 2014-06-25 11:23 336人阅读 评论(0) 收藏
使用Eclipse编辑文件经常出现中文乱码或者文件中有中文不能保存的问题,Eclipse提供了灵活的设置文件编码格式的选项,我们可以通过设置编码 格式解决乱码问题.在Eclipse可以从几个层面设置编 ...
- css3-8 内外边距中的注意要点有哪些
css3-8 内外边距中的注意要点有哪些 一.总结 一句话总结:padding,border都是外延的.margin会合并. 1.两元素样式都有margin:15px,他们中间的距离是15px还是30 ...
- pandas 时间序列分析(一)—— 基础
0. 以时间作为序列的索引 >> from datetime import datetime >> dates = [datetime(2011, 1, i) for i in ...
- Go语言学习(十)bytes包处理字节切片
bytes包提供了对字节切片进行读写操作的一系列函数 字节切片处理的函数比較多,分为基本处理函数,比較函数,后缀检查函数,索引函数,切割函数, 大写和小写处理函数和子切片处理函数等. 1.字节切片基本 ...