direct3d — C ++ Direct3D9 GetFrontBufferData с 16-битной глубиной цвета

В настоящее время я разрабатываю небольшое приложение для скриншотов, которое записывает оба рабочего стола моего экрана в файл.
Я использую функцию GetFrontBufferData (), и она прекрасно работает.

К сожалению, при изменении глубины цвета экрана от 32 до 16 бит (для выполнения некоторых тестов) у меня плохое изображение (фиолетовое изображение с измененным разрешением), и записанный снимок экрана имеет очень низкое качество:

введите описание изображения здесь

Кто-нибудь знает, есть ли способ использовать GetFrontBufferData () с 16-битным экраном?

редактировать:

Мой init Direct3D:

    ZeroMemory(&d3dPresentationParameters,sizeof(D3DPRESENT_PARAMETERS));//Fills a block of memory with zeros.
d3dPresentationParameters.Windowed = TRUE;
d3dPresentationParameters.Flags = D3DPRESENTFLAG_LOCKABLE_BACKBUFFER;
d3dPresentationParameters.BackBufferFormat = d3dFormat;//d3dDisplayMode.Format;//D3DFMT_A8R8G8B8;
d3dPresentationParameters.BackBufferCount = 1;
d3dPresentationParameters.BackBufferHeight = gScreenRect.bottom = uiHeight;
d3dPresentationParameters.BackBufferWidth = gScreenRect.right = uiWidth;
d3dPresentationParameters.MultiSampleType = D3DMULTISAMPLE_NONE;
d3dPresentationParameters.MultiSampleQuality = 0;
d3dPresentationParameters.SwapEffect = D3DSWAPEFFECT_DISCARD;
d3dPresentationParameters.hDeviceWindow = hWnd;
d3dPresentationParameters.PresentationInterval = D3DPRESENT_INTERVAL_DEFAULT;
d3dPresentationParameters.FullScreen_RefreshRateInHz = D3DPRESENT_RATE_DEFAULT;

Поток, который я использую для захвата скриншотов:

    CreateOffscreenPlainSurface(uiWidth, uiHeight, D3DFMT_A8R8G8B8, D3DPOOL_SYSTEMMEM, pBackBuffer, NULL)) != D3D_OK )
{
DBG("Error: CreateOffscreenPlainSurface failed = 0x%x", iRes);
break;
}

GetFrontBufferData(0, pCaptureSurface)) != D3D_OK)
{
DBG("Error: GetFrontBufferData failed = 0x%x", iRes);
break;
}

//D3DXSaveSurfaceToFile("Desktop.bmp", D3DXIFF_BMP, pBackBuffer,NULL, NULL); //Test purposes

ZeroMemory(lockedRect, sizeof(D3DLOCKED_RECT));
LockRect(lockedRect, NULL, D3DLOCK_READONLY)) != D3D_OK )
{
DBG("Error: LockRect failed = 0x%x", iRes);
break;
}

if( (iRes = UnlockRect()) != D3D_OK )
{
DBG("Error: UnlockRect failed = 0x%x", iRes);
break;
}
/**/
  • Этот код прекрасно работает с глубиной цвета 32 бита, но не с 16 битами.
  • При создании устройства я создаю 2 устройства для обоих экранов (iScreenNber). Это также работает в 32 битах (не в 16).
  • При сохранении захваченного снимка экрана в 2 bmp файла для тестирования (в 16 битах) у меня есть один экран, который отлично отображает основной экран, а другой экран черный.
  • При использовании memcpy для использования pData у меня есть скриншот выше с фиолетовым цветом и плохим разрешением

edit2:

Я заметил следующее:

  • При сохранении поверхности вне экрана в файл BMP, я получаю основной экран (на 1.bmp), который обновляется каждый кадр (так что он работает просто отлично). Для второго дисплея я просто получаю первый кадр, потом ничего больше.
  • Цитирование MSDN для GetFrontBufferData «Буфер, на который указывает pDestSurface будет заполнено представлением переднего буфера, преобразованного в стандартный формат 32 бита на пиксель D3DFMT_A8R8G8B8«. Я думаю, что это проблема для 16-битной глубины цвета.
  • Первая проблема связана с memcpy, который не обрабатывает должным образом 16-битную глубину цвета, и я до сих пор не знаю, почему —-> Для этого нужна помощь !!
  • Вторая проблема — второй дисплей, который не работает, и я не знаю почему

Что я здесь не так делаю? Я просто получаю черное изображение в моем файле Desktop N ° xx.bmp

Большое спасибо за Вашу помощь.

1

Решение

Вот как я создаю поверхность для захвата скриншотов:

IDirect3DSurface9* pCaptureSurface = NULL;
HRESULT hr = pD3DDevice->CreateOffscreenPlainSurface(
D3DPresentParams.BackBufferWidth,
D3DPresentParams.BackBufferHeight,
D3DPresentParams.BackBufferFormat,
D3DPOOL_SYSTEMMEM,
&pCaptureSurface,
NULL);
pD3DDevice->GetFrontBufferData(0, pCaptureSurface);

Если вы не хранили D3DPresentParams где угодно, вы можете использовать IDirect3DDevice9::GetDisplayMode чтобы получить ширину, высоту и формат вашей цепочки обмена. Все операции изменения размера и преобразования формата вы можете выполнить после захвата переднего буфера. Кроме того, как я знаю, формат отображения не поддерживает альфа-канал, поэтому обычно D3DFMT_X8R8G8B8не D3DFMT_A8R8G8B8,

Обновить:
На самом деле, вы пытаетесь захватить весь экран с помощью устройства d3d, ничего не рендерив. Цель d3d / opengl — создавать или обрабатывать образы и делать это с GPU-ускорением. Снимок экрана — это просто копирование некоторой видеопамяти, он не использует всю мощность графического процессора. Таким образом, использование любого API-интерфейса графического процессора не дает существенного преимущества. Более того, когда вы захватываете передний буфер, созданный не вами, происходят странные вещи. Чтобы расширить свое приложение, вы можете захватить изображение с помощью GDI, а затем загрузить его в текстуру и выполнить любую постобработку на графическом процессоре.

0

Другие решения

Так что я нашел несколько ответов на мою проблему.

1) Второй монитор не работал, и я не смог сделать снимок экрана с него в 16 битах

Это исходит от memcpy(..) строка в коде. Потому что я работаю с 16-битным монитором, при выполнении memcpyПоверхностная память повреждена, и это приводит к черному экрану.

Я до сих пор не нашел решения для этого, но я работаю над этим.

2) цвета скриншота неправильные

Это, без удивления, связано с глубиной цвета 16 бит. Потому что я использую GetFrontBufferDataи я цитирую Microsoft: Буфер, на который указывает pDestSurface будет заполнено представлением переднего буфера, преобразованного в стандартный формат 32 бита на пиксель D3DFMT_A8R8G8B8. Это означает, что если я хочу использовать данные пикселей из LockRect(...)Я должен «преобразовать» мои данные в 16-битный режим. Поэтому мне нужно конвертировать мои данные pData из D3DFMT_A8R8G8B8 в D3DFMT_R5G6B5 что довольно просто.

3) Как отладить приложение?

Благодаря вашим комментариям мне сказали, что я должен проанализировать pScreeInfo->pData содержание, когда я был в 16 битах (спасибо Niello). Поэтому я создал простой метод, используя необработанные данные из pScreeInfo->pData и копирование в .bmp:

    HRESULT hr;
DWORD dwBytesRead;
UINT uiSize = 1920 * 1080 * 4;
HANDLE hFile;

hFile = CreateFile(TEXT("data.raw"), GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);

BOOL bOk = ReadFile(hFile, pData, uiSize, &dwBytesRead, NULL);

if(!bOk)
exit(0);

pTexture = NULL;
hr = pScreenInfo->g_pD3DDevice->CreateTexture(width, height, 1, 0, D3DFMT_A8R8G8B8, D3DPOOL_MANAGED, &pTexture, NULL);

D3DLOCKED_RECT lockedRect;

hr = pTexture->LockRect(0, &lockedRect, NULL, D3DLOCK_READONLY);

memcpy(lockedRect.pBits, pData, lockedRect.Pitch * height);

hr = pTexture->UnlockRect(0);

hr = D3DXSaveTextureToFile(test, D3DXIFF_BMP, pTexture,NULL);

bOk = CloseHandle(hFile);

SAFE_RELEASE(pTexture);

Этот фрагмент кода позволил мне заметить, что данные pData были правильными, и в конце я смог получить хороший файл .bmp, что означает, что GetFrontBufferData(...) правильно работал, и проблема исходит от memcpy(...)

4) Остальные проблемы

Я все еще пытаюсь узнать, как я могу решить проблему memcpy, чтобы увидеть, откуда возникла проблема. Это последняя проблема, так как цвета сейчас хороши (благодаря преобразованию 32 бит в 16 бит)

Спасибо всем за ваши полезные комментарии!

0

По вопросам рекламы [email protected]