Получение пикселей экрана в виде байтового массива - PullRequest
0 голосов
/ 13 октября 2019

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

Я изменяю код на это:

BitBlt > Image.FromHbitmap(pointer) > LockBits > pixel array

Но я проверяю,можно вырезать какого-то среднего человека и получить что-то вроде этого:

BitBlt > Marshal.Copy > pixel array

Или даже:

WinApi method that gets the screen region as a pixel array

До сих пор я пытался использовать этот код, но безуспешно:

public static byte[] CaptureAsArray(Size size, int positionX, int positionY)
{
    var hDesk = GetDesktopWindow();
    var hSrce = GetWindowDC(hDesk);
    var hDest = CreateCompatibleDC(hSrce);
    var hBmp = CreateCompatibleBitmap(hSrce, (int)size.Width, (int)size.Height);
    var hOldBmp = SelectObject(hDest, hBmp);

    try
    {
        new System.Security.Permissions.UIPermission(System.Security.Permissions.UIPermissionWindow.AllWindows).Demand();

        var b = BitBlt(hDest, 0, 0, (int)size.Width, (int)size.Height, hSrce, positionX, positionY, CopyPixelOperation.SourceCopy | CopyPixelOperation.CaptureBlt);

        var length = 4 * (int)size.Width * (int)size.Height;
        var bytes = new byte[length];

        Marshal.Copy(hBmp, bytes, 0, length);

        //return b ? Image.FromHbitmap(hBmp) : null;
        return bytes;
    }
    finally
    {
        SelectObject(hDest, hOldBmp);
        DeleteObject(hBmp);
        DeleteDC(hDest);
        ReleaseDC(hDesk, hSrce);
    }

    return null;
}

Этот код дает мне System.AccessViolationException при переходе на Marshal.Copy.

Есть ли более эффективный способ получения пикселей экрана в виде байтового массива при использовании BitBlt или аналогичных методов захвата экрана?


РЕДАКТИРОВАТЬ:

Как указано в здесь и как предложено CodyGray, я должен использовать

var b = Native.BitBlt(_compatibleDeviceContext, 0, 0, Width, Height, _windowDeviceContext, Left, Top, Native.CopyPixelOperation.SourceCopy | Native.CopyPixelOperation.CaptureBlt);

var bi = new Native.BITMAPINFOHEADER();
bi.biSize = (uint)Marshal.SizeOf(bi);
bi.biBitCount = 32; 
bi.biClrUsed = 0;
bi.biClrImportant = 0;
bi.biCompression = 0;
bi.biHeight = Height;
bi.biWidth = Width;
bi.biPlanes = 1;

var data = new byte[4 * Width * Height];

Native.GetDIBits(_windowDeviceContext, _compatibleBitmap, 0, (uint)Height, data, ref bi, Native.DIB_Color_Mode.DIB_RGB_COLORS);

My dataВ массиве есть все пиксели скриншота. Теперь я собираюсь проверить, есть ли улучшения производительности или нет.

1 Ответ

1 голос
/ 14 октября 2019

Да, вы не можете просто начать получать доступ к необработанным битам BITMAP объекта через HBITMAP (как возвращено CreateCompatibleBitmap). HBITMAP это просто ручка, как следует из названия. Это не указатель в классическом смысле «С», который указывает на начало массива. Дескрипторы похожи на косвенные указатели.

GetDIBits является подходящим решением для получения необработанного, независимого от устройства массива пикселей из растрового изображения, через которое вы можете перебирать. Но вам все равно придется использовать код, который вам нужен, чтобы получить растровое изображение в первую очередь. По сути, вы хотите что-то вроде это . Конечно, вам нужно будет перевести его на C #, но это не должно быть сложно, так как вы уже знаете, как вызывать функции WinAPI.

Обратите внимание, что вам не нужно вызывать GetDesktopWindow или GetWindowDC. Просто передайте NULL в качестве ручки GetDC;он имеет тот же эффект, что и возврат DC экрана, который затем можно использовать для создания совместимого растрового изображения. В общем, вы почти никогда не должны звонить GetDesktopWindow.

...