views:

577

answers:

3

gdi+ DrawLines function has a clipping bug that can be reproduced by running the following c# code. When running the code two line paths appear that should be identical, because both of them are inside the clipping region, but when the clipping region is set one of the line segment is not drawn.

protected override void OnPaint(PaintEventArgs e)
{
   PointF[] points = new PointF[] { new PointF(73.36f, 196), 
             new PointF(75.44f, 32), 
             new PointF(77.52f, 32), 
             new PointF(79.6f, 196), 
             new PointF(85.84f, 196) };

   Rectangle b = new Rectangle(70, 32, 20, 164);         
   e.Graphics.SetClip(b);
   e.Graphics.DrawLines(Pens.Red, points); // clipped incorrectly
   e.Graphics.TranslateTransform(80, 0);
   e.Graphics.ResetClip();
   e.Graphics.DrawLines(Pens.Red, points);
 }

Setting the antials mode on the graphics object resolves this but that is not a real solution.

Does anybody know of an workaround?

A: 

What appears to be the matter with the code?

OK, the question should be... what should the code do that it doesn't already.

When I run the code, I see 2 red 'spikes' am I not ment to?

You appear to draw the first spike within the clipped rectangle region verified by adding the the following after the declaration of teh Rectangle :

e.Graphics.FillRectangle( new SolidBrush( Color.Black ), b );

Then you perform a translation, reset the clip so at this point I assume the clientRectangle is being used as the appropriate clip region and then attempt to redarw the translated spike. Where's the bug?!?

TK
A: 

The bug is that both line segments should be drawn identical but they are not because the spike that is drawn within the clipping region is completely within the clipping region and should not be clipped in any way but it is. This is a very annoying but that results in any software that uses drawlines heavily + clipping to look unprofessional because of gaps that can appear in the polygons.

Hjörtur
+1  A: 

It appears that this is a known bug...

The following code appears to function as you requested:

protected override void OnPaint(PaintEventArgs e)
    {
        PointF[] points = new PointF[] { new PointF(73.36f, 196), 
         new PointF(75.44f, 32), 
         new PointF(77.52f, 32), 
         new PointF(79.6f, 196), 
         new PointF(85.84f, 196) };

        e.Graphics.SmoothingMode =  System.Drawing.Drawing2D.SmoothingMode.AntiAlias;
        Rectangle b = new Rectangle(70, 32, 20, 165);
        e.Graphics.SetClip(b);
        e.Graphics.DrawLines(Pens.Red, points); // clipped incorrectly
        e.Graphics.TranslateTransform(80, 0);
        e.Graphics.ResetClip();           
        e.Graphics.DrawLines(Pens.Red, points);
    }

Note: I have AntiAlias'ed the line and extended your clipping region by 1

it appears that the following work arounds might help (although not tested):

  • The pen is more than one pixel thick
  • The line is perfectly horizontal or vertical
  • The clipping is against the window boundaries rather than a clip rectangle

The following is a list of articles that might / or then again might not help:

http://www.tech-archive.net/pdf/Archive/Development/microsoft.public.win32.programmer.gdi/2004-08/0350.pdf http://www.tech-archive.net/Archive/Development/microsoft.public.win32.programmer.gdi/2004-08/0368.html

OR...

the following is also possible:

protected override void OnPaint ( PaintEventArgs e )
    {
        PointF[] points = new PointF[] { new PointF(73.36f, 196), 
         new PointF(75.44f, 32), 
         new PointF(77.52f, 32), 
         new PointF(79.6f, 196), 
         new PointF(85.84f, 196) };

        Rectangle b = new Rectangle( 70, 32, 20, 164 );
        Region reg = new Region( b );
        e.Graphics.SetClip( reg, System.Drawing.Drawing2D.CombineMode.Union);
        e.Graphics.DrawLines( Pens.Red, points ); // clipped incorrectly
        e.Graphics.TranslateTransform( 80, 0 );
        e.Graphics.ResetClip();
        e.Graphics.DrawLines( Pens.Red, points );
    }

This effecivly clips using a region combined/unioned (I think) with the ClientRectangle of the canvas/Control. As the region is difned from the rectangle, the results should be what is expected. This code can be proven to work by adding

e.Graphics.FillRectangle( new SolidBrush( Color.Black ), b );

after the setClip() call. This clearly shows the black rectangle only appearing in the clipped region.

This could be a valid workaround if Anti-Aliasing the line is not an option.

Hope this helps

TK