Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Taking a screenshot using Graphics.CopyFromScreen with 150% scaling

Tags:

c#

.net

bitmap

I'm trying to create a screenshot/bitmap of my screen. I wrote this function:

public static Bitmap CreateScreenshot(Rectangle bounds)
{
    var bmpScreenshot = new Bitmap(bounds.Width, bounds.Height, 
                                   PixelFormat.Format32bppArgb);
    var gfxScreenshot = Graphics.FromImage(bmpScreenshot);
    gfxScreenshot.CopyFromScreen(bounds.X, bounds.Y,
        0, 0,
        new Size(bounds.Size.Width, bounds.Size.Height),
        CopyPixelOperation.SourceCopy);
    return bmpScreenshot;
}

This function is being called in my overlay form that should draw the bitmap onto itself. I'm currently using GDI+ for the whole process.

private void ScreenshotOverlay_Load(object sender, EventArgs e)
{
    foreach (Screen screen in Screen.AllScreens)
        Size += screen.Bounds.Size;
    Location = Screen.PrimaryScreen.Bounds.Location;

    _screenshot = BitmapHelper.CreateScreenshot(new Rectangle(new Point(0, 0), Size));
    Invalidate(); // The screenshot/bitmap is drawn here
}

Yep, I dispose the bitmap later, so don't worry. ;) On my laptop and desktop computer this works fine. I've tested this with different resolutions and the calculations are correct. I can see an image of the screen on the form.

The problem starts with the Surface 3. All elements are being scaled by a factor of 1.5 (150%). This consequently means that the DPI changes. If I try to take a screenshot there, it does only capture like the upper-left part of the screen but not the whole one. I've made my way through Google and StackOverflow and tried out different things:

  1. Get the DPI, divide it by 96 and multiply the size components (X and Y) of the screen with this factor.
  2. Add an entry to application.manifest to make the application DPI-aware.

The first way did not bring the desired result. The second one did, but the whole application would have to be adjusted then and this is quite complicated in Windows Forms.

Now my question would be: Is there any way to capture a screenshot of the whole screen, even if it is has a scalation factor higher than 1 (higher DPI)? There must be a way to do this in order to make it working everywhere.

But at this point I had no real search results that could help me. Thanks in advance.

like image 217
Dominic B. Avatar asked Sep 07 '15 12:09

Dominic B.


2 Answers

Try this, which is found within SharpAVI's library. It works well on devices regardless of resolution scale. And I have tested it on Surface 3 at 150%.

System.Windows.Media.Matrix toDevice;
using (var source = new HwndSource(new HwndSourceParameters()))
{
    toDevice = source.CompositionTarget.TransformToDevice;
}
screenWidth = (int)Math.Round(SystemParameters.PrimaryScreenWidth * toDevice.M11);
screenHeight = (int)Math.Round(SystemParameters.PrimaryScreenHeight * toDevice.M22);

SharpAVI can be found here: https://github.com/baSSiLL/SharpAvi It is for videos but uses a similar copyFromScreen method when getting each frame:

graphics.CopyFromScreen(0, 0, 0, 0, new System.Drawing.Size(screenWidth, screenHeight));
like image 160
ickydime Avatar answered Sep 29 '22 02:09

ickydime


Before taking your screen shot, you can make the process DPI aware:

[System.Runtime.InteropServices.DllImport("user32.dll")]
public static extern bool SetProcessDPIAware();

private static Bitmap Screenshot()
{
    SetProcessDPIAware();
    var screen = System.Windows.Forms.Screen.PrimaryScreen;
    var rect = screen.Bounds;
    var size = rect.Size;

    Bitmap bmpScreenshot = new Bitmap(size.Width, size.Height);
    Graphics g = Graphics.FromImage(bmpScreenshot);
    g.CopyFromScreen(0, 0, 0, 0, size);

    return bmpScreenshot;
}
like image 20
Alexander Higgins Avatar answered Sep 29 '22 02:09

Alexander Higgins