Welcome to WuJiGu Developer Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
660 views
in Technique[技术] by (71.8m points)

winforms - Creating different brush patterns in c#

I'm trying to make something similar to paint. I'm trying to figure out how make different brush styles. Like in Paint 3D you get a certain line fills when using the pen tool vs using the paint brush tool.

enter image description here

I have no idea where to even start. I've spent a good portion of the day looking through documentations, and watching YouTube videos. I'm more lost than when I started. The closest thing I came across was line caps, but that's definitely not what I'm looking for.

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Answer

0 votes
by (71.8m points)

!!See the UPDATE below!!

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

To help you further here a few points to observe:

  • TextureBrush is a brush, not a pen. So you can't follow a path, like the mouse movements to draw along that curve. Instead, you need to find an area to fill with the brush.

  • This also implies that you need to decide how and when to trigger the drawing; basic options are by time and/or by distance. Usually, the user can set parameters for these often called 'flow' and 'distance'..

  • Instead of filling a simple shape and drawing many of those, you can keep adding the shapes to a GraphicsPath and fill that path.

  • To create a TextureBrush you need a pattern file that has transparency. You can either make some or download them from the web where loads of them are around, many for free.

  • Most are in the Photoshop Brush format 'abr'; if they are not too recent (<=CS5) you can use abrMate to convert them to png files.

  • You can load a set of brushes to an ImageList, set up for large enough size (max 256x256) and 32bpp to allow alpha.

  • Most patterns are black with alpha, so if you want color you need to create a colored version of the current brush image (maybe using a ColorMatrix).

  • You may also want to change its transparency (best also with the ColorMatrix).

  • And you will want to change the size to the current brush size.


Update

After doing a few tests I have to retract the original assumption that a TextureBrush is a suitable tool for drawing with textured tips.

It is OK for filling areas, but for drawing free-hand style it will not work properly. There are several reasons..:

  • one is that the TextureBrush will always tile the pattern in some way, flipped or not and this will always look like you are revealing one big underlying pattern instead of piling paint with several strokes.

  • Another is that finding the area to fill is rather problematic.

  • Also, tips may or may not be square but unless you fill with a rectangle there will be gaps.

See here for an example of what you don't want at work.

The solution is really simple and much of the above still applies:

  • What you do is pretty much regular drawing but in the end, you do a DrawImage with the prepared 'brush' pattern.

Regular drawing involves:

  • A List<List<Point>> curves that hold all the finished mouse paths
  • A List<Point> curentCurve for the current path

In the Paint event you draw all the curves and, if it has any points, also the current path.

For drawing with a pattern, it is necessary to also know when to draw which pattern version.

If we make sure not to leak them we can cache the brush patterns..:

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

This is a simple/simplistic caching method. For better efficiency you could use a Dictionary<string, Bitmap> with a naming scheme that produces a string from the pattern index, size, color, alpha and maybe a rotation angle; this way each pattern would be stored only once.

Here is an example at work:

enter image description here

A few notes:

In the MouseDown we create a new current curve:

curCurve = new Tuple<Bitmap, List<Point>>(brushPattern, new List<Point>());
curCurve.Item2.Add(e.Location);

In the MouseUp I add the current curve to the curves list:

 curves.Add(new Tuple<Bitmap, List<Point>>(curCurve.Item1, curCurve.Item2.ToList()));

Since we want to clear the current curve, we need to copy its points list; this is achieved by the ToList() call!

In the MouseMove we simply add a new point to it:

if (e.Button == MouseButtons.Left)
{
    curCurve.Item2.Add(e.Location);
    panel1.Invalidate();
}

The Paint goes over all curves including the current one:

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();
}

It makes sure the patterns are drawn centered.

The ListView is set to SmallIcons and its SmallImageList points to a smaller copy of the original ImageList.

It is important to make the Panel Doublebuffered! to avoid flicker!


Update: Instead of a Panel, which is a Container control and not really meant to draw onto you can use a Picturebox or a Label (with Autosize=false); both have the DoubleBuffered property turned on out of the box and support drawing better than Panels do.

Btw: The above quick and dirty example has only 200 (uncommented) lines. Adding brush rotation, preview, a stepping distance, a save button and implementing the brushes cache takes it to 300 lines.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome to WuJiGu Developer Q&A Community for programmer and developer-Open, Learning and Share
...