Ошибка скриншота С#?

Я использую следующий код, чтобы сделать снимок экрана:

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);

Теперь это работает замечательно, и с ним легко работать, однако на некоторых скриншотах я всегда встречаю маленькие "белые точки". Это может быть очень раздражающим и искажать изображение, когда оно происходит в больших количествах.

Мне удалось сузить проблему, и когда я попытаюсь сделать снимок экрана следующего изображения, возникает ошибка:

bug-causing image

Результат снимка экрана выглядит следующим образом:

bug

Как вы можете это исправить? И из любопытства, как это объясняется?

В моей тестовой среде скриншот вообще не сохраняется. Я напрямую использую его со следующим кодом:

pictureBox1.Image = bmp;

tl; dr Я пытаюсь сделать скриншоты, а некоторые пиксели заменены на белый и искажают результат.

Спасибо вам заблаговременно.

EDIT: Оказывается, растровое изображение делает область прозрачной (белый цвет получается из фонового цвета формы, спасибо за обнаружение этого спредера!)

bug with different background

Но очевидно, как вы можете ясно видеть на первой картинке; Я не пытаюсь захватить прозрачный контент. Почему это делается?

EDIT2:

Это весь класс, который я использую, чтобы выбрать снимок экрана:

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);
    }
}

В моей форме я иду:

pictureBox1.Image = SnippingTool.Snip();

Ответы

Ответ 1

Для тех, кто сталкивается с этой проблемой; это конфигурация, которая в конечном итоге отлично работает для меня:

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

Ответ 2

Проблема заключается в том, что мы снимаем экран в ARGB, и захваченное изображение будет иметь прозрачность.

Затем мы сохраним его как JPG, а прозрачность станет белой.

Чтобы решить проблему, нам просто нужно создать экземпляр Bitmap с явным пиксельным форматом, который НЕ включает альфа:

Bitmap screenBmp = new Bitmap(width, height, PixelFormat.Format32bppRgb)