BitBlt и Direct3D для снимков экрана C#

Я работаю над программой для создания снимков экрана окон из дескриптора окна. Я начинаю с создания снимка экрана с помощью BitBlt, и если это не удается, когда в окне используется ускоренная графика, я переключаюсь на использование Direct3D.

Для справки, вот код BitBlt:

private Bitmap BitBltWindow(Rectangle rect, IntPtr window)
{
    const uint srcCopy = 0x00CC0020;

    IntPtr hWndDc = GetDC(window);
    IntPtr hMemDc = CreateCompatibleDC(hWndDc);
    IntPtr hBitmap = CreateCompatibleBitmap(hWndDc, rect.Width, rect.Height);
    SelectObject(hMemDc, hBitmap);

    BitBlt(hMemDc, 0, 0, rect.Width, rect.Height, hWndDc, 0, 0, srcCopy);
    Bitmap bitmap = Bitmap.FromHbitmap(hBitmap);

    DeleteObject(hBitmap);
    ReleaseDC(window, hWndDc);
    ReleaseDC(IntPtr.Zero, hMemDc);
    DeleteDC(hWndDc);

    return bitmap;
}

И вот мой метод определить, пуст ли он:

private bool IsBitmapBlack(Bitmap bmp)
{
    Rectangle rect = new Rectangle(0, 0, bmp.Width, bmp.Height);
    BitmapData bmpData = bmp.LockBits(rect, ImageLockMode.ReadWrite, bmp.PixelFormat);

    IntPtr ptr = bmpData.Scan0;
    int rowSize = bmpData.Stride < 0 ? -bmpData.Stride : bmpData.Stride;
    
    // Scanning for non-zero bytes
    bool allBlack = true;
    for (int y = 0; y < bmp.Height; y++)
    {
        byte[] pixels = new byte[rowSize];
        Marshal.Copy(IntPtr.Add(ptr, y * bmpData.Stride), pixels, 0, rowSize);
        
        if (pixels.Max() > 0)
        {
            allBlack = false;
            break;
        }
    }
    bmp.UnlockBits(bmpData);

    return allBlack;
}

Мой вопрос заключается в том, как определить, произошел ли сбой BitBlt или нет. В таких окнах, как Chrome, вызов BitBlt просто возвращает пустое растровое изображение, это легко обнаружить, и я переключаюсь на Direct3D. Но в таких окнах, как Visual Studio 2022, похоже, используется смесь графики, поэтому возвращаемое растровое изображение не пустое, но в нем отсутствуют биты, например:

Тогда как та же запись с поверхности Direct3D выглядит так:

Кто-нибудь знает вызов Windows API, который я могу использовать, чтобы определить, использует ли окно какое-либо количество ускоренной графики?

Возможно, проблема в Chrome и/или на что указывает «указатель Windows» в каждом случае. VS — это приложение WPF, о котором я слышал в последний раз. Какой метод поддерживает или не поддерживает WPF; и/или так ли это должно быть.

Gerry Schmitz 26.05.2024 21:01

BitBlt работает только с неускоренной графикой, т.е. просто считывает изображение из памяти устройства. Поэтому он не работает в Chrome. Direct3D может считывать память из графического процессора и памяти устройства, поэтому работает в любом окне, которое я тестировал до сих пор. Указатель на контекст устройства ненулевой во всех случаях, просто нет изображения для захвата из памяти при наличии ускоренной графики. Я хочу знать, смогу ли я определить, использует ли окно ускоренную графику.

Mathew Rimmington 26.05.2024 23:29
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
2
101
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

После публикации вопроса я решил продолжить изучение процесса, чтобы посмотреть, смогу ли я определить, содержат ли загруженные модули какие-либо известные как Direct3D. Я списал это на неудачный эксперимент: получить список модулей было достаточно легко, и это работало на некоторых окнах, но не на всех окнах, на которых я это пробовал. например Хром.

Я решил преобразовать свой метод IsBitmapBlack в метод, который определяет, является ли окно частично черным. Это позволяет обойти проблему Visual Studio, в которой отсутствуют некоторые биты.

Новый метод теперь выглядит так:

private bool IsBitmapPartBlack(Bitmap bmp)
{
    int height = bmp.Height;
    int width = bmp.Width;
    Rectangle rect = new Rectangle(0, 0, bmp.Width, height);
    BitmapData bmpData = bmp.LockBits(rect, ImageLockMode.ReadWrite, bmp.PixelFormat);

    IntPtr ptr = bmpData.Scan0;
    int rowSize = bmpData.Stride < 0 ? -bmpData.Stride : bmpData.Stride;
        
    // Scanning for non-zero bytes
    bool partBlack = false;
    int blackRowCount = 0;
    for (int y = 0; y < height; y++)
    {
        byte[] pixels = new byte[rowSize];
        Marshal.Copy(IntPtr.Add(ptr, y * bmpData.Stride), pixels, 0, rowSize);

        if (pixels.FirstOrDefault(pixel => pixel > 0) == 0)
        { 
            blackRowCount++;
            if (blackRowCount >= height * .01)  // If 1% of the bitmap rows are black then treat the image as part black
            {
                partBlack = true;
                break;
            }
        }
    }
    bmp.UnlockBits(bmpData);

    return partBlack;
}

Другие вопросы по теме