Home > Blockchain >  In OnPaint() event, how to check if a given rectangle intersects with the invalidated region?
In OnPaint() event, how to check if a given rectangle intersects with the invalidated region?

Time:11-30

In Winforms, it's possible to create a region that is not a rectangle and invalidate it this way :

Region region = new Region(new Rectangle(...));
region.Union(new Rectangle(...));                   
Invalidate(region);     

Then, in OnPaint() event, only the region invalidated above will be drawn again:

protected override void OnPaint(PaintEventArgs e)
{   
    //will only repaint the region invalidated above
    //even if ClipRectangle area is bigger than that
    e.Graphics.FillRectangle(Brushes.Blue, e.ClipRectangle); 
}

Inside OnPaint() event, is there a way to check if a given rectangle intersect with the invalidated area ?

I could use rectangle.Intersect(e.ClipRectangle) but this might gave false positives.

EDIT : it seems what I want is possible using the GetUpdateRgn Win32 function (AFAIK there is no direct Winforms equivalent of that function)

CodePudding user response:

private List<Rectangle> rectangleList;    

When you invalidate, you set your rectangleList:

rectangleList = getRectangles(...);
Region region = new Region(new Rectangle(...));
foreach(Rectangle rect in rectangleList)
{
    region.Union(rect);  
}                 
Invalidate(region);     

And in the paint method, you check if any Rectangles in your rectangleList intersect, then you clear it:

protected override void OnPaint(PaintEventArgs e)
{   
    bool intersection = false;
    foreach(Rectangle rect in rectangleList)
    {
        if(e.ClipRectangle.Intersect(rect) 
        {
            intersection = true;
            break;
        }
    } 

    if(intersection)
    {
        rectangleList.Clear();
        DoIntersectionStuff();
    }
    else
    {
        DoNonIntersectionStuff();
    }
}

CodePudding user response:

I answer my own question : It's possible to get the update region by calling GetUpdateRgn() function before BeginPaint. You need to call IsVisible() to know if rectangle is inside region. Which means it will performs a GDI api call (unlike Rectangle.Intersect()) and so it's just as slow as calling GDI drawing function directly and let it discard the result (eg : DrawText(), FillRect()).

Region region;    
protected override void WndProc(ref Message m) 
{   
    switch (m.Msg)
    {
        case WM_PAINT:
            region = null;
            IntPtr hrgn = CreateRectRgn(0, 0, 0, 0);
            try
            {
                int result = GetUpdateRgn(Handle, hrgn, false);
                if (result == SIMPLEREGION || region == COMPLEXREGION)
                {
                    region = Region.FromHrgn(hrgn); 
                }
            }
            finally
            {
                DeleteObject(hrgn);
            }
            break;                
    } 

    base.WndProc(ref m);
}

protected override void OnPaint(PaintEventArgs e)
{       
    if (region != null && region.IsVisible(rectangle))
    {
        //...
    }
}

Win32 native functions declaration:

[DllImport("gdi32.dll")]
static extern IntPtr CreateRectRgn(int left, int top, int right, int bottom);

[DllImport("user32.dll")]
static extern int GetUpdateRgn(IntPtr hWnd, IntPtr hRgn, bool bErase);

[DllImport("gdi32.dll")]
static extern bool DeleteObject(IntPtr hObject);

const int WM_PAINT = 0x000F;
const int SIMPLEREGION = 2;
const int COMPLEXREGION = 3;
  • Related