views:

49

answers:

3

I paint a drawing on a drawing area.

In order to optimize the performance of that drawing I decided to redraw only inside the really necessary region(clipping region). See the picture.

The problem is that I don't arrive to build a method(GetBitmapFromRectangle) that returns me the result of intersection of my paths with the clipping rectangle. This method could be very useful when the user moves(with the mouse) paths. In that case there is need to repaint only the former and current moved path area - and not the whole picture, that in case of complex pictures can visibly slow down the application performance.

My tests shows that the calculation time is much less important that the drawing one, so I'd better perform more calculations that more drawing.

I want to draw not the entire paths that intersects the rectangle, but really only the paths inside the clipping region.

alt text

In other words, I need a method that will make the BLUE lines inside the red area PINK.

Here is the code:

using System.Drawing;
using System.Windows.Forms;

namespace WindowsApplication38
{
    public partial class Form1 : Form
    {
        Point center;
        int radius, penWidth;

        public Form1()
        {
            InitializeComponent();
            center = new Point(50, 50);
            radius = 100;
            penWidth = 5;
            this.ResizeRedraw = true;
        }

        protected override void OnPaint(PaintEventArgs e)
        {
            base.OnPaint(e);

            e.Graphics.Clear(this.BackColor);

            e.Graphics.DrawImage(
                GetBitmapFromRectangle(e.ClipRectangle),
                e.ClipRectangle.Location);

            //
            // INITIAL DRAWING METHOD
            //            
            Pen p = new Pen(Color.Blue, penWidth);
            // draw O
            e.Graphics.DrawEllipse(p, center.X - radius, center.Y - radius, radius * 2, radius * 2);
            // draw X
            Point[] line1 = new Point[] { new Point(0, 0), new Point(this.Width, this.Height) };
            Point[] line2 = new Point[] { new Point(this.Width, 0), new Point(0, this.Height) };
            e.Graphics.DrawLines(p, line1);
            e.Graphics.DrawLines(p, line2);
            p.Dispose();
        }

        private Bitmap GetBitmapFromRectangle(Rectangle rect)
        {
            Bitmap bmp = new Bitmap(rect.Width, rect.Height);
            Graphics g = Graphics.FromImage(bmp);
            if (rect != this.DisplayRectangle)
                g.Clear(Color.Red);
            else
                g.Clear(this.BackColor);

            // Draw ONLY! the intersetion between drawing and rectangle...
            // How to ???

            return bmp;
        }
    }
}

Nota bene

The example is a sample for demo purpose only. In the real project I have very complex graphic, and the drawing time is much more expensive that the calculation one. This why I want to not redraw all the painting, but only the lines inside the clipping region!

A: 

Before doing this, I suggest testing the performance improvement by manually culling the set and comparing frame rates with and without culling. You may find that GDI+ already optimises anything you draw outside the viewport.

If you find that it is helpful to cull, the simplest approach is to find the bounding box of each object and test for rectangle intersection:

bool intersect = left1 < right2 && left2 < right1 && top1 < bottom2 && top2 < bottom1.

Depending on the type of drawings, this may have a high rate of false hits, such as a large hollow circle that goes around the viewport, but it usually does well enough for most purposes.

Marcelo Cantos
Maybe, however I'll use GetBitmapFromRectangle(...) for updating the graphic in situations like moving with mouse a graphic element inside the drawing.
serhio
your "code" left right has no sense. Look in the provided example, how could you apply your test?
serhio
Do you know what "find the bounding box" means? In the case of the ellipse, `left1 = center.X - radius; right1 = center.X + radius; top1 = center.Y - radius; bottom1 = center.Y + radius;` and `(left/right/top/bottom)2` correspond to the clipping region. Before down-voting someone who's answer you don't understand, you might want to give them an opportunity to address your concerns.
Marcelo Cantos
@Marcelo Cantos: your answer have nothing with I've asked. Fist of all, your "bounding box" serves to nothing - the intersection with bounding box does not mean a intersection with the path - for identify the intersection is Region object for. Secondly, I need the intersection path, not the fact itself of intersection.
serhio
Why on earth do you need that? Tell GDI to clip the region and just draw the whole ellipse! I'm done with this question.
Marcelo Cantos
not only the whole ellipse, but also the whole line, and all the whole big paths that cross the little clipping rectangle... You see, you have no answer.
serhio
"Tell GDI to clip the region" what should this mean? Now, say I have a little (5x5) rectangle that is moved with the mouse on that screen. need I repaint ALL the graphic, if I moved the 5x5 from 0,5 to 0,10?
serhio
and, please, if you have no answer, remove it.
serhio
He has an answer, and a pretty good one too.
Pondidum
@Pondidum: who has... what do you mean? nobody told me how to draw the pink lines inside the red rectangle.
serhio
@serhio: I did, but you are too busy trashing my attempts to help you to realise this. Pearls before swine.
Marcelo Cantos
@Marcelo Cantos: did you read my comments? you answer how to detect if 2 rectangles are intersected - elementary thing. I ask how to find the intersection path between a rectangle and a original path.
serhio
@serhio: No, you asked how to update a rectangular region in a drawing. It's right there in the title and throughout your question. You may not like the solution I offered (presumably because it doesn't fit your preconceived notion of how it should be done) but it *is* a solution. FWIW, your fat pen makes a precise computation ridiculously complex, even ignoring the inconvenient fact that a single ellipse can have up to four disjoint arcs intersecting the region; it also makes it pretty much impossible to ensure that the drawn arcs and segments stay precisely inside the region.
Marcelo Cantos
@Marcelo: You say that, because you don't know well .NET GDI. There is a method of GraphicsPath Widen that "inflate" path with a pen settings, and then there you create a Region from that path, and use IsVisible for a point or rectangle. Once again, I don't ask about detecting intersection, because I know how to detect it.
serhio
A: 

I would copy existing drawing code to GetBitmapFromRectangle method without any changes. Required optimization is already done by reducing the updated area. Existing code, copied to GetBitmapFromRectangle, just draws some lines outside of the bitmap bounds, this is OK - Graphics class can handle this. Testing every drawing primitive for intersiction with clipping rectangle only reduces the program performance.

Alex Farber
don't agree. figure parallel(or worse curved) lines all panel width and a slim vertical clipping rectangle.And, after my tests, drawing time takes munch more that the calculation one.
serhio
If your actual drawing is much more complicated, this makes sence. In this case you need some math, specific to your algorithms - like simple test shown by Marcelo Cantos. But this is impossible to say without looking at your actual drawing code.
Alex Farber
The code I gave is close to the code I use. I need just to obtain the intersection paths in the code above.
serhio
A: 

You should check out the GraphicsPath class in System.Drawing.Drawing2D.

fallenidol
GraphicsPath really is the only way in GDI+ to do what you need.
fallenidol