在 C# 中创建不同的画笔图案

Creating different brush patterns in c#

我正在尝试制作类似于绘画的东西。我想弄清楚如何制作不同的画笔样式。就像在 Paint 3D 中一样,使用钢笔工具与使用画笔工具时会得到特定的线条填充。

我什至不知道从哪里开始。我一天中的大部分时间都在浏览文档和观看 YouTube 视频。我比刚开始时更迷茫。我遇到的最接近的东西是线帽,但这绝对不是我要找的东西。

!!请参阅下面的更新!!

Hans' link should point you in the right direction, namely toward TextureBrushes.

为了帮助您进一步观察这里的几点:

  • TextureBrush是画笔,不是笔。所以你不能跟随一条路径,就像鼠标移动一样沿着那条曲线绘制。相反,您需要找到一个区域来填充画笔。

  • 这也意味着您需要决定如何以及何时触发绘制;基本选项是按时间 and/or 按距离。通常,用户可以为这些通常称为'flow'和'distance'..

  • 设置参数
  • 您可以继续将形状添加到 GraphicsPath 并填充该路径,而不是填充一个简单的形状并绘制其中的许多形状。

  • 要创建 TextureBrush 您需要一个具有透明度的图案文件。您可以制作一些或从网上下载它们,那里有很多,很多都是免费的。

  • 大部分为 Photoshop 笔刷格式 'abr';如果它们不是太新 (<=CS5),您可以使用 abrMate 将它们转换为 png 文件。

  • 您可以将一组画笔加载到 ImageList,设置足够大的尺寸(最大 256x256)和 32bpp 以允许 alpha。

  • 大多数图案都是带有 alpha 的黑色,所以如果你想要颜色,你需要创建当前画笔图像的彩色版本(可能使用 ColorMatrix)。

  • 您可能还想更改其透明度(最好也使用 ColorMatrix)。

  • 并且您需要将大小更改为当前画笔大小。


更新

在做了一些测试后,我不得不收回最初的假设,即 TextureBrush 是适合使用纹理笔尖进行绘图的工具。

填充区域没问题,但绘制free-hand样式就不能正常工作了。有几个原因..:[=​​34=]

  • 一个是 TextureBrush 总是会以某种方式 tile 图案,翻转与否,这看起来总是像是在揭示一个大的底层图案而不是用几笔画打桩油漆。

  • 还有一个就是找填充区域比较麻烦

  • 此外,提示可能是也可能不是正方形,但除非您用矩形填充,否则会有间隙。

请参阅 ,了解您在工作中想要什么的示例。

解决方案非常简单,上面的大部分内容仍然适用:

  • 你所做的几乎是常规绘图,但最后,你用准备好的 'brush' 图案做了一个 DrawImage

常规绘图涉及:

  • A List<List<Point>> curves 包含所有已完成的鼠标路径
  • 一个List<Point> curentCurve为当前路径

Paint 事件中绘制所有曲线,如果有任何点,还有当前路径。

用图案画,还需要知道什么时候哪个图案版本

如果我们确保不泄露它们,我们可以缓存画笔图案..:[=​​34=]

Bitmap brushPattern = null;
List<Tuple<Bitmap,List<Point>>> curves = new List<Tuple<Bitmap,List<Point>>>();
Tuple<Bitmap, List<Point>> curCurve = null;

这是一种simple/simplistic缓存方法。为了提高效率,您可以使用 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 的较小副本。

重要的是让面板避免闪烁!


更新: 而不是 Panel,这是一个 Container 控件,并不是真正要在上面绘制,您可以使用 PictureboxLabelAutosize=false);两者都开箱即用 DoubleBuffered 属性 并且比 Panels 更好地支持绘图。

顺便说一句:上面的快速粗略示例只有 200 行(未注释)。添加画笔旋转、预览、步进距离、保存按钮并实现画笔缓存使其达到 300 行。