Search code examples
c#drawinggdi+widescreen

Clipping a graphic in memory before drawing on form


I'm trying to use the following code to copy a portion of the screen to a new location on my Windows form.

private void Form1_Paint(object sender, PaintEventArgs e)
{
    var srcPoint = new Point(0,0);
    var dstPoint = new Point(Screen.PrimaryScreen.Bounds.Width/2, Screen.PrimaryScreen.Bounds.Height/2);
    var copySize = new Size(100, 100);

    e.Graphics.CopyFromScreen(srcPoint, dstPoint, copySize, CopyPixelOperation.SourceCopy);
}

The CopyFromScreen function appears to ignore any clips set before it.

e.SetClip(new Rectangle(srcPoint.X, srcPoint.Y, 20, 20));

Am I doing something wrong or is this just the wrong approach.

For context: I'm trying to mitigate a UI widescreen game issue by copying the HUD at the edges and to be centered closer to the middle.

I am aware of FlawlessWidescreen, but it doesn't support many less popular games. I suppose poking around in memory (what flawless does) could also work but is almost always against TOS.

Edit: final goal is to copy some arbitrary path as the shape rather than a simple rectangle (I was hoping from an image mask).

Edit #2: So I have an irregular shape being drawn every 100ms. It turns out it just bogs the game down until I slow it down to every 500ms. But still the game isn't smooth. Is this operation of copying and drawing an image just going to be too heavy of an operation in GDI+? I was thinking it was simple enough to not bog anything down.

Thoughts before I mark the answer as accepted?


Solution

  • I guess it is indeed the wrong approach.

    The ClippingRegion is only used for clipping the DrawXXX and FillXXX commands, including DrawImage (!).

    The CopyFromScreen however will use the given Points and Size and not clip the source.

    For a Rectangle region this is no problem since you can achieve the same result by choosing the right Point and Size values.

    But once you aim at using more interesting clipping regions you will have to use an intermediate Bitmap onto which you copy from the screen and from which you can then use DrawImage into the clipped region.

    For this you can create more or less complicated GraphicsPaths.

    Here is a code example:

    After putting the cliping coordinates into a Rectangleor GraphicsPath clip you can write something like this:

    e.Graphics.SetClip(clip);
    
    using (Bitmap bitmap = new Bitmap(ClientSize.Width, ClientSize.Height))
    {
        using (Graphics G = Graphics.FromImage(bitmap))
                G.CopyFromScreen(dstPoint, srcPoint, 
                                 copySize, CopyPixelOperation.SourceCopy);
        e.Graphics.DrawImage(bitmap, 0, 0);
    }