!!查看下面的更新!!
Hans' link 应该为您指明正确的方向,即指向TextureBrushes。
为了帮助您进一步了解以下几点:
TextureBrush 是画笔,而不是钢笔。所以你不能沿着一条路径走,就像鼠标移动沿着那条曲线画一样。相反,您需要找到一个区域来填充画笔。
这也意味着你需要决定如何以及何时触发绘图;基本选项是按时间和/或按距离。通常,用户可以为这些通常称为“流量”和“距离”的参数设置参数。..
您可以继续将形状添加到 GraphicsPath 并填充该路径,而不是填充简单的形状并绘制其中的许多形状。
要创建 TextureBrush 您需要一个具有透明度的图案文件。您可以制作一些或从网络上下载它们,其中很多都是免费的。
大多数都是 Photoshop 画笔格式“abr”;如果它们不是太新 (abrMate 将它们转换为 png 文件。
您可以将一组画笔加载到 ImageList,设置为足够大的尺寸(最大 256x256)和 32bpp 以允许 alpha。
大多数图案都是带有 alpha 的黑色,因此如果您想要颜色,您需要创建当前画笔图像的彩色版本(可能使用 ColorMatrix)。
您可能还想更改其透明度(最好也使用 ColorMatrix)。
并且您需要将大小更改为当前画笔大小。
更新
在做了一些测试之后,我不得不收回原来的假设,即 TextureBrush 是一种适合使用纹理笔尖进行绘图的工具。
填充区域可以,但是徒手画就不行了。有几个原因..:
请参阅here,了解您在工作中不想要什么的示例。
解决方案非常简单,上面的大部分内容仍然适用:
- 您所做的几乎是常规绘图,但最后,您使用准备好的“画笔”图案绘制
DrawImage。
常规绘图涉及:
- 一个
List<List<Point>> curves 包含所有完成的鼠标路径
- 当前路径的
List<Point> curentCurve
在Paint 事件中,您绘制所有曲线,如果有任何点,还绘制当前路径。
对于带图案的绘图,还需要知道何时绘制哪个图案版本。
如果我们确保不泄露它们,我们可以缓存画笔图案..:
Bitmap brushPattern = null;
List<Tuple<Bitmap,List<Point>>> curves = new List<Tuple<Bitmap,List<Point>>>();
Tuple<Bitmap, List<Point>> curCurve = null;
这是一种简单/简单的缓存方法。为了提高效率,您可以使用带有命名方案的Dictionary<string, Bitmap>,该方案从模式索引、大小、颜色、alpha 和可能的旋转角度生成字符串;这样每个模式将只存储一次。
这是一个工作示例:
几点说明:
在 MouseDown 我们创建一个新的电流曲线:
curCurve = new Tuple<Bitmap, List<Point>>(brushPattern, new List<Point>());
curCurve.Item2.Add(e.Location);
在 MouseUp 中,我将当前曲线添加到曲线列表中:
curves.Add(new Tuple<Bitmap, List<Point>>(curCurve.Item1, curCurve.Item2.ToList()));
由于我们要清除当前曲线,我们需要复制它的点列表;这是通过ToList() 调用实现的!
在 MouseMove 中,我们只需为其添加一个新点:
if (e.Button == MouseButtons.Left)
{
curCurve.Item2.Add(e.Location);
panel1.Invalidate();
}
Paint 遍历所有曲线,包括当前曲线:
for (int c = 0; c < curves.Count; c++)
{
e.Graphics.TranslateTransform(-curves[c].Item1.Width / 2, -curves[c].Item1.Height / 2);
foreach (var p in curves[c].Item2)
e.Graphics.DrawImage(curves[c].Item1, p);
e.Graphics.ResetTransform();
}
if (curCurve != null && curCurve.Item2.Count > 0)
{
e.Graphics.TranslateTransform(-curCurve.Item1.Width / 2, -curCurve.Item1.Height / 2);
foreach (var p in curCurve.Item2)
e.Graphics.DrawImage(curCurve.Item1, p);
e.Graphics.ResetTransform();
}
确保图案居中绘制。
ListView 设置为 SmallIcons,其 SmallImageList 指向原始 ImageList 的较小副本。
重要的是让面板Doublebuffered!避免闪烁!
更新: 可以使用 Picturebox 或 Label(与 Autosize=false );两者都具有开箱即用的DoubleBuffered 属性,并且比Panels 更支持绘图。
顺便说一句:上述快速而肮脏的示例只有 200 行(未注释)。添加画笔旋转、预览、步进距离、保存按钮并实现画笔缓存使其达到 300 行。