14
votes

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();
2
Does this involve video players?CodesInChaos
Just so I am clear, you are trying to take a screenshot of a black screen but when generated it's white?James
This rather looks like the captured image has transparency. Supposing the background color of pictureBox1 was set to black?spender
Looks like a video driver bug to me, look for an update. CopyFromScreen() is not entirely squeaky-clean either, you could try this code. But I doubt that's the issue here.Hans Passant
@John: Try new Bitmap(rc.Width, rc.Height, PixelFormat.Format24bppRgb);leppie

2 Answers

3
votes

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;
}
1
votes

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)