14

I use the following code to take a screenshot:

var rc = SystemInformation.VirtualScreen;
Bitmap bmp = new Bitmap(rc.Width, rc.Height);
Graphics g = Graphics.FromImage(bmp);
g.CopyFromScreen(rc.Left, rc.Top, 0, 0, bmp.Size, CopyPixelOperation.SourceCopy);

Now this works wonderfully and it's easy to work with, however I always come across little 'white dots' in some of the screenshots. This can be very annoying and distort the image when it occurs in larger quantities.

I managed to narrow down the issue and when I try to take a screenshot of the following image the bug occurs:

bug-causing image

The output of the screenshot looks like this:

bug

How can you fix this? And out of curiosity, how is this explained?

In my testing environment the screenshot isn't saved at all. I directly use it with the following code:

pictureBox1.Image = bmp;

tl;dr I'm trying to take screenshots and some of the pixels are replaced with white and distort the result.

Thank you very much in advance.

EDIT: It turns out that the bitmap makes the area transparent (white comes from the background color of the form, thanks for spotting this spender!)

bug with different background

But obviously as you can clearly see in the first picture; I'm not trying to capture any transparent content. Why does it do this?

EDIT2:

This is the whole class I'm using to select my screenshot:

public partial class SnippingTool : Form
{
    public static Image Snip()
    {
        var rc = SystemInformation.VirtualScreen;

        Bitmap bmp = new Bitmap(rc.Width, rc.Height);
        Graphics g = Graphics.FromImage(bmp);
        g.CopyFromScreen(rc.Left, rc.Top, 0, 0, bmp.Size, CopyPixelOperation.SourceCopy);

        var snipper = new SnippingTool(bmp);

        if (snipper.ShowDialog() == DialogResult.OK)
        {
            return snipper.Image;
        }

        return null;
    }

    public SnippingTool(Image screenShot)
    {
        InitializeComponent();
        this.BackgroundImage = screenShot;
        this.ShowInTaskbar = false;
        this.FormBorderStyle = FormBorderStyle.None;
        this.StartPosition = FormStartPosition.Manual;

        int screenLeft = SystemInformation.VirtualScreen.Left;
        int screenTop = SystemInformation.VirtualScreen.Top;
        int screenWidth = SystemInformation.VirtualScreen.Width;
        int screenHeight = SystemInformation.VirtualScreen.Height;

        this.Size = new System.Drawing.Size(screenWidth, screenHeight);
        this.Location = new System.Drawing.Point(screenLeft, screenTop);


        this.DoubleBuffered = true;
    }

    public Image Image { get; set; }

    private Rectangle rcSelect = new Rectangle();
    private Point pntStart;

    protected override void OnMouseDown(MouseEventArgs e)
    {
        if (e.Button != MouseButtons.Left) return;
        pntStart = e.Location;
        rcSelect = new Rectangle(e.Location, new Size(0, 0));
        this.Invalidate();
    }

    protected override void OnMouseMove(MouseEventArgs e)
    {
        if (e.Button != MouseButtons.Left) return;
        int x1 = Math.Min(e.X, pntStart.X);
        int y1 = Math.Min(e.Y, pntStart.Y);
        int x2 = Math.Max(e.X, pntStart.X);
        int y2 = Math.Max(e.Y, pntStart.Y);
        rcSelect = new Rectangle(x1, y1, x2 - x1, y2 - y1);
        this.Invalidate();
    }

    protected override void OnMouseUp(MouseEventArgs e)
    {
        if (rcSelect.Width <= 0 || rcSelect.Height <= 0) return;
        Image = new Bitmap(rcSelect.Width, rcSelect.Height);
        using (Graphics gr = Graphics.FromImage(Image))
        {
            gr.DrawImage(this.BackgroundImage, new Rectangle(0, 0, Image.Width, Image.Height),
                rcSelect, GraphicsUnit.Pixel);
        }
        DialogResult = DialogResult.OK;
    }

    protected override void OnPaint(PaintEventArgs e)
    {
        using (Brush br = new SolidBrush(Color.FromArgb(120, Color.Black)))
        {
            int x1 = rcSelect.X; int x2 = rcSelect.X + rcSelect.Width;
            int y1 = rcSelect.Y; int y2 = rcSelect.Y + rcSelect.Height;
            e.Graphics.FillRectangle(br, new Rectangle(0, 0, x1, this.Height));
            e.Graphics.FillRectangle(br, new Rectangle(x2, 0, this.Width - x2, this.Height));
            e.Graphics.FillRectangle(br, new Rectangle(x1, 0, x2 - x1, y1));
            e.Graphics.FillRectangle(br, new Rectangle(x1, y2, x2 - x1, this.Height - y2));
        }
        using (Pen pen = new Pen(Color.Red, 3))
        {
            e.Graphics.DrawRectangle(pen, rcSelect);
        }
    }

    protected override bool ProcessCmdKey(ref Message msg, Keys keyData)
    {
        if (keyData == Keys.Escape) this.DialogResult = DialogResult.Cancel;
        return base.ProcessCmdKey(ref msg, keyData);
    }
}

On my form I then go:

pictureBox1.Image = SnippingTool.Snip();
John
  • 181
  • 9
  • Are you saving as PNG? It looks like you are. Try saving as JPG – Matt Evans Jan 26 '15 at 12:53
  • How is the file being saved? – James Jan 26 '15 at 12:53
  • 6
    Does this involve video players? – CodesInChaos Jan 26 '15 at 12:53
  • Thank you for the comments, I've added more information on this in the post. – John Jan 26 '15 at 12:56
  • 1
    Just so I am clear, you are trying to take a screenshot of a black screen but when generated it's white? – James Jan 26 '15 at 12:58
  • 4
    This rather looks like the captured image has transparency. Supposing the background color of `pictureBox1` was set to black? – spender Jan 26 '15 at 12:59
  • In a way, yes. Although if you look closely you can see that it is a dark shade within an even darker shade. I think it's the combination that causes the bug. – John Jan 26 '15 at 12:59
  • What exactly is the screenshot of? Something GPU-accelerated like flash or video? – Karl-Johan Sjögren Jan 26 '15 at 13:00
  • Just a simple image on Paint. – John Jan 26 '15 at 13:01
  • spender, you are definitely onto something! I just changed the background color of the form and the white has changed to that color. (Reference: http://i.imgur.com/zLOaLcM.png). But obviously the image I'm trying to take a picture of doesn't have any transparency in it. Why does it save it with transparency? – John Jan 26 '15 at 13:02
  • can you present code of how you draw your picture? – Yuriy Zaletskyy Jan 26 '15 at 13:11
  • It looks to me like it is down to the `CopyPixelOperation.SourceCopy`, according to [MSDN](https://msdn.microsoft.com/en-us/library/system.drawing.copypixeloperation(v=vs.110).aspx), this will copy directly from the source so if the image you are copying has complete opacity then this will be copied... does changing this parameter help? (i.e `CopyPixelOperation.CaptureBlt`) – Sayse Jan 26 '15 at 13:17
  • Changing it to that makes my screen go completely black. – John Jan 26 '15 at 13:18
  • On my form I then go: at which event of your form you do this? – Yuriy Zaletskyy Jan 26 '15 at 13:24
  • 3
    Looks like a video driver bug to me, look for an update. CopyFromScreen() is not entirely squeaky-clean either, you could try [this code](http://stackoverflow.com/a/3072580/17034). But I doubt that's the issue here. – Hans Passant Jan 26 '15 at 13:26
  • John, if you are able to (with gimp or something) create an image that has different opacities in it and see how it looks when a screenshot is taken with your program.... did any of the other enumerations make a difference? the documentation uses `MergePaint` – Sayse Jan 26 '15 at 13:27
  • Hans I tried the other code and it produces the exact same result. – John Jan 26 '15 at 13:33
  • I don't think it's a driver bug. I can reproduce the problem the same way – Claudio P Jan 26 '15 at 13:35
  • Definitely not a driver problem. I've had people on different machines confirm this bug. – John Jan 26 '15 at 13:35
  • 2
    @John: Try `new Bitmap(rc.Width, rc.Height, PixelFormat.Format24bppRgb);` – leppie Jan 26 '15 at 13:52
  • That does it! The bug is gone. What does this property do, exactly? Because it seems to have a major effect on the quality of the image, which is unfortunate. (Here's a Putin for reference: http://i.imgur.com/YyipZOt.png) – John Jan 26 '15 at 14:00
  • `CopyFromScreen` doesn't really work with anything but software surfaces. Anything drawn in Overlay mode will end up being "a window" into the overlain surface (that's why it's called "overlay" I guess). I assume that the new MSPaint exploits hardware accelerated drawing, which is not a "surface", and doesn't get captured by `CopyFromScreen`. You might remember trying to save video frames through MSPaint - and MSPaint showing you a moving video in the same spot. Saving that produces a black image, of course. – Luaan Jan 26 '15 at 14:01
  • Interesting, that should have only gotten rid of the alpha part of the bitmap. Are you using some color scheme other than 32-bit? Or are you running in 16-bit color mode or something? – Luaan Jan 26 '15 at 14:03
  • Nope, Windows is running in True Color (32 bit) mode. But the degrade in graphics is very noticeable. Putin isn't happy. – John Jan 26 '15 at 14:04
  • @John: Did you do it for both `Bitmap` creations? Play with `CopyPixelOperation.CaptureBlt` in combination too. Try `Format32bppRgb`or `Gdi` or `Canonical` too – leppie Jan 26 '15 at 14:41

2 Answers2

3

For anyone who comes across this issue; this is the configuration that ended up working fine for me:

var rc = SystemInformation.VirtualScreen;

using (Bitmap bmp = new Bitmap(rc.Width, rc.Height, PixelFormat.Format32bppRgb))
{
    using (Graphics g = Graphics.FromImage(bmp))
    {
        g.CopyFromScreen(rc.Left, rc.Top, 0, 0, bmp.Size);
    }
    using (var snipper = new SnippingTool(bmp))
    {
        if (snipper.ShowDialog() == DialogResult.OK)
        {
            return snipper.Image;
        }
    }
    return null;
}
VMAtm
  • 27,943
  • 17
  • 79
  • 125
John
  • 181
  • 9
1

The problem is that we were capturing the screen in ARGB, and the captured image would come with transparency.

Then we'd save it as a JPG, and the transparency would become white.

To solve the problem, we just need to instantiate the Bitmap with an explicit PixelFormat that does NOT include alpha:

Bitmap screenBmp = new Bitmap(width, height, PixelFormat.Format32bppRgb)
ANeves
  • 6,219
  • 3
  • 39
  • 63