I have a simple highlighter using a Pen
object. When the size is very large, it creates odd stripes which you can fix by defining its Start & End Cap withSystem.Drawing.Drawing2D.LineCap.Round
but it instantly overlaps itself and loses its transparency. A flat cap also loses its transparency when you draw over itself multiple times.
How can I create an opaque pen that does not overlap itself or create stripes with a large pen width?
private static Color baseColor = Color.Yellow;
bool Draw;
Graphics g;
Point start;
Point end;
private void Form1_Load(object sender, EventArgs e)
{
g = this.CreateGraphics();
Pen1.Color = Color.FromArgb(128, baseColor);
Pen1.Width = 50;
Pen1.StartCap = System.Drawing.Drawing2D.LineCap.Flat; //I know it's default, just for clarification's sake
Pen1.EndCap = System.Drawing.Drawing2D.LineCap.Flat; //' '''' '' ' ''''''' '''' ''' ''''''''''''' ' ''''
}
private void Form1_MouseDown(object sender, MouseEventArgs e)
{
Draw = true;
start = e.Location;
}
private void Form1_MouseUp(object sender, MouseEventArgs e)
{
Draw = false;
}
private void Form1_MouseMove(object sender, MouseEventArgs e)
{
if (Draw == true)
{
end = e.Location;
g.DrawLine(Pen1, start, end);
start = end;
}
}
Note: Yes, I am aware .CreateGraphics
is not a good drawing method, it has a unique purpose that is irrelevant to the question.
You have two issues with the drawing objects:
You draw each Line separately; this will create ugly artifacts where the common points overlap. Instead draw the lines all in one go with the
DrawLines
method! (Note the plural!)You did not restrict the
Pen.MiterLimit
; this creates ugly spikes when the lines directions change sharply. Try to restrict it to around 1/2 of thePen.Width
or less . SettingLineJoin
is also recommened as well a the twoCaps
..Collect the points of your current line in a
List<Point> currentPoints
in theMouseMove
and collect uponMouseUp
thecurrentList
into aList<List<Point>> allPointLists
!then you can draw both in the
Paint
event..Note that it will pay immediately to do it right, i.e. draw only with a valid graphics object and always rely on the
Paint
event to make sure that the drawing is always updated as needed! Usingcontrol.CreateGraphics
is almost always wrong and will hurt as soon as you go beyond a single non-persistent drawing operation..Here is the full code:
Here is the
Paint
event; note that I use two differentPens
for the currently drawn lines and the older ones. Also note that you can useDrawCurve
instead ofDrawLines
to get even smoother results..Also note that I use a
List<T>
to be flexible wrt the number of elements and that I convert it to an array in theDraw
commands..To prevent flicker don't forget to turn on
DoubleBuffered
for yourForm
; or use aDoubleBuffered Panel
subclass or simply aPictureBox
!Final note: I left out the case of simple clicks; if they are supposed to paint you will have to catch them, probably best in the
if (points.Count > 1)
check and best do aFillEllipse
at the right spot and with the right size..Update
List<T>
is so useful I hardly ever usearrays
theses days. Here is how to make use of it to implement aClear
and anUndo
button:Note two small corrections in the
MouseUp
code this has required to handle thecurrentPoints
list properly! The clearing is obvious; theToList()
call is making a copy of the data, so we don't clear the instance we have just added to te List of Lists!