WPF 粉笔绘制
在做白板书写的时候,会有各种笔的绘制,比如 书写笔、马克笔、演示笔等等。粉笔的功能需求也是很有必要的。
上网搜了一圈,几乎没有绘制粉笔的。
有的是毛笔、楷体等绘制的如下博客:
wpf inkcanvas customink 毛笔效果_wpf inkcanvas 笔锋-CSDN博客
绘制粉笔的思路,一开始是源于 github的一个仓库:mychalkboard/MyChalkBoard: MyChalkBoard is an application for you to quickly sketch with a chalk.
对应的网页的链接:MyChalkBoard
思路:就是用一个通用的笔头(ImageSource),利用Stroke 捕获到的StylusPoints的点,生成对应的点的坐标,调用drawingContext.DrawImage,绘制图案
1、生成笔头
找UI绘制一个粉笔形状的图片,以Png为例:
2、支持修改颜色
参考了该博文: 2018-8-10-WPF-修改图片颜色-CSDN博客
只要是修改 WriteableBitmap 的RGBA的值,达到替换颜色的效果
public static unsafe ImageSource ConvertImageColor(Color newColor, WriteableBitmap writableBitmap)
{
var bitmap = writableBitmap;
if (bitmap == null)
{
return null;
}
bitmap.Lock();
var length = bitmap.PixelWidth * bitmap.PixelHeight *
bitmap.Format.BitsPerPixel / 8;
var backBuffer = (byte*)bitmap.BackBuffer;
var byteList = new byte[length];
for (int i = 0; i + 4 < length; i = i + 4)
{
byteList[i] = newColor.B;
byteList[i + 1] = newColor.G;
byteList[i + 2] = newColor.R;
byteList[i + 3] = backBuffer[i + 3];
}
bitmap.Unlock();
bitmap = new WriteableBitmap(bitmap.PixelWidth, bitmap.PixelHeight, 96, 96,
bitmap.Format, bitmap.Palette);
bitmap.Lock();
bitmap.WritePixels(new Int32Rect(0, 0, bitmap.PixelWidth, bitmap.PixelHeight),
byteList, bitmap.BackBufferStride, 0);
bitmap.AddDirtyRect(new Int32Rect(0, 0, bitmap.PixelWidth, bitmap.PixelHeight));
bitmap.Unlock();
return bitmap;
}
3、收集点迹
主要是调用了我们组内自研封装的一套基于鼠标、触摸汇总的笔迹点迹收集的算法(WPF 输入附加事件 - 唐宋元明清2188 - 博客园),通过 Down、Move、Up以及分段汇总的方式,收集并呈现笔迹
4、自定义补点
由于通过设备采集到的点,会有疏密的区分,所以对于比较稀疏的点,需要通过补点的方式,达到减少锯齿的效果,具体的补点的距离,因业务需要不同,可以通过调节参数的方式做适配
//标识上一个点
var previousPoint = new Point(double.NegativeInfinity, double.NegativeInfinity);
for (int i = 0; i < stylusPoints.Count; i++)
{
var pressureFactor = stylusPoints[i].PressureFactor * 2;
var currentPoint = stylusPoints[i].ToPoint();
var vector = previousPoint - currentPoint;
var newWidth = width * pressureFactor;
//作为基准值
var baseWidth = newWidth / 1.5;
if (!double.IsInfinity(vector.Length) && vector.Length > baseWidth)
{
var w2 = newWidth;
if (newWidth - vector.Length > newWidth)
w2 = newWidth - vector.Length; var newPointCount = (int)(vector.Length / (baseWidth)) * 2;
var dx = (currentPoint.X - previousPoint.X) / newPointCount;
var dy = (currentPoint.Y - previousPoint.Y) / newPointCount; for (int pointCount = 0; pointCount < newPointCount; pointCount++)
{
var newX = previousPoint.X + dx * (pointCount + 1);
var newY = previousPoint.Y + dy * (pointCount + 1);
drawingContext.DrawImage(imageSource, new Rect(newX - w2, newY - w2, w2 * 2, w2 * 2));
}
}
else
{
Rect rectangle = new Rect(currentPoint.X - newWidth, currentPoint.Y - newWidth, newWidth * 2, newWidth * 2);
drawingContext.DrawImage(imageSource, rectangle);
}
previousPoint = currentPoint;
5、绘制点迹
通过以上4个前提步骤,就可以计算出来笔迹的大小,通过调用 drawingContext.DrawImage 的方式把带粉笔头的Image绘制出来
drawingContext.DrawImage(imageSource, new Rect(newX - w2, newY - w2, w2 * 2, w2 * 2));
6、效果如下:
普通书写
重力慢速书写(仿压着粉笔写字):
WPF 粉笔绘制的更多相关文章
- wpf 后台绘制圆弧
wpf 前台绘制圆弧很简单,如:<Path x:Name="path_data" Stroke="#FFE23838" StrokeThickness=& ...
- C#WPF 如何绘制几何图形 图示教程 绘制sin曲线 正弦 绘制2D坐标系 有图有代码
原文:C#WPF 如何绘制几何图形 图示教程 绘制sin曲线 正弦 绘制2D坐标系 有图有代码 C#WPF 如何绘制几何图形? 怎么绘制坐标系?绘制sin曲线(正弦曲线)? 这离不开Path(Syst ...
- WPF 图形绘制 及各种线帽、箭头的实现
原文:WPF 图形绘制 及各种线帽.箭头的实现 /// <summary> /// 矩形类 /// </summary> public sealed ...
- WPF特效-绘制实时2D激光雷达图
原文:WPF特效-绘制实时2D激光雷达图 接前两篇: https://blog.csdn.net/u013224722/article/details/80738619 https://blog.cs ...
- 【C#】第3章补充(一)如何在WPF中绘制正弦曲线
分类:C#.VS2015 创建日期:2016-06-19 使用教材:(十二五国家级规划教材)<C#程序设计及应用教程>(第3版) 一.要点 本例子提前使用了教材第13章介绍的基本知识. 二 ...
- 在WPF中绘制多维数据集
原文 https://stuff.seans.com/2008/08/13/drawing-a-cube-in-wpf/ 是时候使用WPF绘制一个简单的3D对象了.作为WPF中3D图形的快速介绍,让我 ...
- WPF 如何绘制不规则按钮,并且有效点击范围也是不规则的
最近在做一个东西,如地图,点击地图上的某一区域,这一区域需要填充成其他颜色.区域是不规则的,而且点击该区域的任一点,都能够变色.普通的按钮只是简单的加载一幅图肯定是不行的.查了很多资料,终于把它搞定了 ...
- WPF拖动绘制
using System; using System.Windows; using System.Windows.Controls; using System.Windows.Input; using ...
- wpf GeometryDrawing 绘制文字
<GeometryDrawing x:Key="GeometryDrawingText"> <GeometryDrawing.Geometry> <R ...
- WPF绘制折线
WPF后台绘制折线,填充到一个GRID下 private void btnPreview_Click(object sender, RoutedEventArgs e) { GridImg.Child ...
随机推荐
- Docker - 在docker中部署Nginx
1.docker search 查找ngix 2.docker pull下载镜像 3.查看镜像列表 4.docker run启动容器 5.测试nginx容器是否启动成功 1.docker search ...
- 读论文-电子商务产品推荐的序列推荐系统综述与分类(A Survey and Taxonomy of Sequential Recommender Systems for E-commerce Product Recommendation)
前言 今天读的这篇文章是于2023年发表在"SN Computer Science"上的一篇论文,这篇文章主要对序列推荐系统进行了全面的调查和分类,特别是在电子商务领域的应用.文章 ...
- 面试题 17.12. BiNode
地址:https://leetcode-cn.com/problems/binode-lcci/ <?php /** 二叉树数据结构TreeNode可用来表示单向链表(其中left置空,righ ...
- Java Properties配置文件和XML配置文件读取
一.properties类读取配置文件 1.从项目中读取properties配置文件,新建一个main程序,对应位置建立一个名称为config.properties配置文件,随意放置一些键值对.IDE ...
- Swagger OpenAPI Schema 为空时 Example Value 显示 "string" 的原因及解决方案
解决Swagger UI示例值显示"string"的问题 最近在使用ObjectScript生成JSON接口文档时,遇到了一个奇怪的问题: 生成的JSON数据是正常的. 但Swag ...
- 程序员必看 Linux 常用命令(重要)
文件操作命令 find find 用于在指定目录下查找文件或子目录,如果不指定查找目录,则在当前目录下查找 命令格式:find path -option [-print] [ -exec/-ok co ...
- 如何通过 MCP 将你的 Supabase 数据库连接到 Cursor
Cursor + MCP + Supabase. 图片来自作者 在过去几周里,MCP(Model Context Protocol,模型上下文协议)在许多 AI 相关的在线社区和论坛里大火.开发者和技 ...
- 【Linux】3.7 定时任务调度
3.7定时任务调度 1. 任务调度原理 crond任务调度:crontab进行定时任务调度 使用方法:crontab [选项] crontab [选项] -e:编辑crontab定时任务 -i:查询c ...
- ASP.NET 日志路径
默认路径 protected void Button_StreamWrite_Click(object sender, EventArgs e) { StreamWriter sw = new ...
- unity手机花屏
关于Camera组件中Clear Flags的理解 - 知乎 (zhihu.com) https://blog.csdn.net/yanchezuo/article/details/77337755 ...