WPF InkCanvas 毛笔效果

首先贴出本文参考学习的文章吧。

https://www.cnblogs.com/LCHL/p/9055642.html#4206298

感谢这位懒羊羊的代码和讲解(下简称羊博主),我在此基础上稍微加了一些东西,希望能使书写效果得到更好的提升吧。建议先从羊博主的博文看起。


前文
  • 本文致力于解决羊博主的2-3问题,即代码粗细度的优化。
  • 本文本着分享知识的目的写出,希望能对一些寻找类似知识的人一些帮助。笔者为WPF初学者,能得到各位的批评指正也是荣幸万分。
  • 因工作公司的原因,无法贴出全部代码,我尽量在下文中对自己的思路进行详细的讲解。(要吃饭的呀)
  • 文章也提出了一些笔者尚未解决的问题,希望能在分享自己学习知识的同时,也能得到各位大神的解疑。

思路

1. 卡顿问题

羊博主提出使用WPF中的Freeze()方法将画刷“冻结”。

Freezable 类提供特殊功能,以便在使用修改或复制开销很大的对象时帮助提高应用程序性能。

笔者使用该方法后,书写性能得到了很大提升,但是书写点数目达到万为单位时,出现书写不流畅的情况,且越书写越卡顿。笔者冥思苦想三天三夜,尝试了很多方法都没有得到解决。果然皇天不负有心人,终于还是让我找到了一些改进方法。代码如下:

ImageDrawing imageDrawing = new ImageDrawing()
{
    ImageSource = imageSource,
    Rect = new Rect(afterChangeX - lastThickness / 2.0, afterChangeY - lastThickness / 2.0, lastThickness, lastThickness)
    //lastThickness为当前的粗细度;afterChangeX为当前的X坐标,afterChangeY为当前Y坐标
};
    imageDrawing.Freeze();
    drawingContext.DrawDrawing(imageDrawing);

DrawImage更改为DrawDrawing,书写性能确实得到了虽然微弱但可见的提升。(自己给自己鼓掌)但是具体有多少提升,笔者并没有做量化。(太懒了)
效果基本满意,如果您有更好的解决方法,希望能得到您的指点。

2. 颜色问题

羊博主博客已完美解决该问题,棒棒哒。??ヽ(°▽°)ノ?)

3. 粗细问题

  • 粗细度问题可以分解为两个问题,一个是书写时间,一个是书写距离。简而言之就是书写速度越快,笔迹越细;书写速度越慢,笔迹越粗。(速度v = 距离s / 时间t,这个大家应该都知道)

3.1 书写距离

书写距离应该怎么获得?

--- 无非就是两个点之间的距离。

  1. 在InkCanvas下重写OnDraw方法(详见羊博主博文
  2. 在OnDraw方法中带了参数StylusPointCollection,该参数为书写点的搜集。该参数搜集动态绘制时产生的点,一般为两个点,即绘制一条线段时的前后两个点,这两个点相减就得到了两点间的距离。

3.2 书写时间

这个参数在Ondraw中没有,如果想关联该参数的话可以研究一下RawStylusInput.Timestamp,(项目组的大神把这些都搭建好了,我用就完事了)该参数可以获取发生输入的时间,两点时间相减就获得了间距的书写时间。两点就是两条线段的终点,因为在绘制时是连续的线,所以后面的线的起点和前面线的终点为相同的点,这个应该很好理解。

3.3 书写速度

通过距离除以时间得出书写速度,自己规定一个速度阈值,在大于这个阈值时,将粗细值缩小;小于这个阈值时,将粗细度放大。该阈值就看你自己想要书写的效果定。建议将进行加减时的值设置为定值,这样线条变化更加平滑。

以下代码不包含时间参数的影响,即只考虑书写间距的影响。所以大家都能用。

部分代码如下:

                Point nextPoint = (Point)stylusPoints[1];   //线段的终点
                Vector distance = nextPoint - lastPoint;    //两点间距
                if (distance.Length != 0)
                {
                    double add_Y_UnitAmount = (nextPoint.Y - lastPoint.Y) / distance.Length;   //Y轴上的单位变化量
                    double add_X_UnitAmount = (nextPoint.X - lastPoint.X) / distance.Length;
                    double afterChangeX = lastPoint.X;
                    double afterChangeY = lastPoint.Y;
                    for (double j = 0; j <= distance.Length; j += 10)   //分割线段,化线为点,对每个点进行处理,达到粗细平滑变化的效果
                        //这个值为什么是10我真没搞懂。。。。
                        //之前写的1,后来莫名其妙改成了10,结果线条平滑了许多,然后我就默默把我的贝赛尔曲线优化给取消了。。。
                        //我只知道这个值越大,线条越平滑。但是太大了,线条比较短了。具体实验一下就知道了。。
                    {
                        if (Convert.ToInt32(distance.Length) > lastDistance)  //前个线段的长度大于当前线段的情况,让线条变细
                        {
                            lastThickness = (lastThickness - _subThicknessRate) <= _minThicness ? _minThicness : lastThickness - _subThicknessRate;
                        }
                        else if (lastThickness < _thickness) //前个线段的长度大于当前线段,且小于规定的最大宽度
                        {
                            lastThickness = (lastThickness + _addThicknessRate) >= _thickness ? _thickness : lastThickness + _addThicknessRate;
                        }
                        afterChangeX += add_X_UnitAmount;
                        afterChangeY += add_Y_UnitAmount;

                        ImageDrawing imageDrawing = new ImageDrawing()
                        {
                            ImageSource = imageSource,
                            Rect = new Rect(afterChangeX - lastThickness / 2.0, afterChangeY - lastThickness / 2.0, lastThickness, lastThickness)
                        };
                        imageDrawing.Freeze();
                        drawingContext.DrawDrawing(imageDrawing);  //绘制
                    }
                    lastPoint = new Point(afterChangeX, afterChangeY);  //保存最后一个优化点,即终点
                    lastDistance = Convert.ToInt32(distance.Length);
                    actrualThickness = lastThickness;
                }

静态呈现方法与之相同,只要理解了上述的方法,应该就没什么大问题了。笔者就不在此赘述。
效果图:



可以在最小的粗细度的时候判断是否还是超过现在的线段长度,是的话可以让afterChangeX 和afterChangeY 这两个值直接等于最后的点,从而实现书写过快时的流线间断感。但是线段会不平滑。(图画的比较丑,没有表现出间断的优美感。。。)
效果图:

if (lastThickness == _minThicness)
{
     afterChangeX += add_X_UnitAmount * (distance.Length - j - 1);
     afterChangeY += add_Y_UnitAmount * (distance.Length - j - 1);
     break;
}


打完收工。

有疑问请留言。

原文地址:https://www.cnblogs.com/younShieh/p/10602787.html

时间: 2024-11-03 18:13:18

WPF InkCanvas 毛笔效果的相关文章

wpf 模拟3D效果(和手机浏览图片效果相似)(附源码)

原文 wpf 模拟3D效果(和手机浏览图片效果相似)(附源码) pf的3D是一个很有意思的东西,类似于ps的效果,类似于电影动画的效果,因为动画的效果,(对于3D基础的摄像机,光源,之类不介绍,对于依赖属性也不介绍.),个人认为,依赖属性这个东西,有百分之五十是为了3D而存在.(自己写的类似于demo的东西)先上图,无图无真相这是demo的整个效果图,可以用鼠标移动,触摸屏也可以手指滑动,图片会移动,然后移动结束,会有一个回弹的判断. <Window x:Class="_3Dshow.Wi

WPF实现毛玻璃效果

1和2需要Microsoft.WindowsAPICodePack.Shell.dll 和引用using System.Windows.Interop,并只能在有DwmApi.dll 版本的Windows操作系统下使用.这两种方法的共同缺点是:在启动窗体时会一闪. 一. [StructLayout(LayoutKind.Sequential)] public struct MARGINS { public int cxLeftWidth; public int cxRightWidth; pub

WPF 实现水纹效果

原文:WPF 实现水纹效果 鼠标滑过产生水纹,效果图如下: ? ? XMAL就放置了一个img标签 ? 后台主要代码 窗体加载: private void Window_Loaded(object sender, RoutedEventArgs e) { Bitmap bmp = Properties.Resources.water; ww = new WaterWave(bmp); //设置显示大小和图片一样 this.gInfo.Width = bmp.Width; this.gInfo.H

WPF实现抽屉效果

原文:WPF实现抽屉效果 界面代码(xaml): <Window x:Class="TransAnimation.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="MainWindow" Hei

android中实现毛笔效果(View 中绘图)

最近有一个项目设计一个APP实现通过触摸屏实现毛笔写字效果.传统的绘画板程序直接通过Path的moveTo和LineTo便可实现简单的线条绘画程序.然而要达到毛笔的笔锋效果则需要更为详细点的设计.我的实现思路是通过以触摸事件DOWN.MOVE.UP中的每一个点为圆心画圆,除此之外还可通过触摸压力的大小改变所绘圆的半径,这样一连串的圆便可粗略地模拟毛笔笔锋效果,在设计次效果时遇到两个问题: 1:android触摸中的MOVE时间取点的频率不是很高,会隔一定的像素取点,当轻触滑动时会出现一天不连续圆

WPF元素可视化效果

在WPF中设置元素的可视化效果主要用到BlurEffect类和DropShadowEffect类.(目前只学到这两个,哈哈) 1.BlurEffect类 命名空间: System.Windows.Media.Effects. BlurEffect 使目标纹理模糊的位图效果. 程序集:PresentationCore(在 PresentationCore.dll 中) 用于 XAML 的 XMLNS:http://schemas.microsoft.com/winfx/2006/xaml/pres

WPF实现选项卡效果(2)——动态添加AvalonDock选项卡

简介 在前面一篇文章里面,我们使用AvalonDock实现了类似于VS的选项卡(或者浏览器的选项卡)效果.但是我们是通过xaml代码实现. 现在我们尝试通过C#代码实现选项卡的动态添加. 修改Xaml代码 在前面一篇文章的Xaml代码里面,添加如下代码: <Grid.RowDefinitions> <RowDefinition Height="25"></RowDefinition> <RowDefinition></RowDefi

WPF实现选项卡效果(1)——使用AvalonDock

简介 公司最近一个项目,软件采用WPF开发,需要实现类似于VS的选项卡(或者是浏览器的选项卡)效果.搜寻诸多资料后,发现很多同仁推荐AvalonDock这款开源控件.在其官方地址下载源码和Demo后,对其进行了初步的研究,初步实现了预期效果. AvalonDocking的结构树 在下载的Demo中,我们可以发现AvalonDock的可视化结构树如下: <avalon:DockingManager x:Name="dockingManager"> <avalon:Lay

android中实现毛笔效果(View 中画图)

近期有一个项目设计一个APP实现通过触摸屏实现毛笔写字效果.传统的绘画板程序直接通过Path的moveTo和LineTo便可实现简单的线条绘画程序.然而要达到毛笔的笔锋效果则须要更为具体点的设计.我的实现思路是通过以触摸事件DOWN.MOVE.UP中的每个点为圆心画圆,除此之外还可通过触摸压力的大小改变所绘圆的半径,这样一连串的圆便可粗略地模拟毛笔笔锋效果,在设计次效果时遇到两个问题: 1:android触摸中的MOVE时间取点的频率不是非常高,会隔一定的像素取点.当轻触滑动时会出现一天不连续圆