[host] Fixed the DXGIManager to capture the cursor properly.

Note: This class will be entirely re-written when I find some time,
it is very poorly implemented, full of assumptions and creates a new
texture for every single frame 🤦.
This commit is contained in:
Geoffrey McRae 2017-11-03 00:18:37 +11:00
parent 7d83af9410
commit ffd2fc1824
8 changed files with 856 additions and 13 deletions

3
.gitmodules vendored
View File

@ -1,6 +1,3 @@
[submodule "vendor/kvm-guest-drivers-windows"]
path = vendor/kvm-guest-drivers-windows
url = https://github.com/virtio-win/kvm-guest-drivers-windows.git
[submodule "vendor/DXGICaptureSample"]
path = vendor/DXGICaptureSample
url = https://github.com/pgurenko/DXGICaptureSample.git

View File

@ -19,7 +19,7 @@ Place, Suite 330, Boston, MA 02111-1307 USA
#pragma once
#include "ICapture.h"
#include <vendor\DXGICaptureSample\DXGICaptureSample\DXGIManager.h>
#include <vendor\DXGICapture\DXGIManager.h>
#define W32_LEAN_AND_MEAN
#include <windows.h>

View File

@ -96,7 +96,7 @@
<Link>
<SubSystem>Windows</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
<AdditionalDependencies>kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;setupapi.lib;d3d11.lib;dxgi.lib;gdiplus.lib;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalDependencies>kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;setupapi.lib;d3d11.lib;dxgi.lib;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalLibraryDirectories>%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
</Link>
</ItemDefinitionGroup>
@ -113,7 +113,7 @@
<Link>
<SubSystem>Windows</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
<AdditionalDependencies>kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;setupapi.lib;d3d11.lib;dxgi.lib;gdiplus.lib;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalDependencies>kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;setupapi.lib;d3d11.lib;dxgi.lib;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalLibraryDirectories>%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
</Link>
</ItemDefinitionGroup>
@ -134,7 +134,7 @@
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
<GenerateDebugInformation>true</GenerateDebugInformation>
<AdditionalDependencies>kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;setupapi.lib;d3d11.lib;dxgi.lib;gdiplus.lib;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalDependencies>kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;setupapi.lib;d3d11.lib;dxgi.lib;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalLibraryDirectories>%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
</Link>
</ItemDefinitionGroup>
@ -155,12 +155,12 @@
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
<GenerateDebugInformation>true</GenerateDebugInformation>
<AdditionalDependencies>kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;setupapi.lib;d3d11.lib;dxgi.lib;gdiplus.lib;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalDependencies>kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;setupapi.lib;d3d11.lib;dxgi.lib;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalLibraryDirectories>%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
</Link>
</ItemDefinitionGroup>
<ItemGroup>
<ClCompile Include="..\vendor\DXGICaptureSample\DXGICaptureSample\DXGIManager.cpp" />
<ClCompile Include="..\vendor\DXGICapture\DXGIManager.cpp" />
<ClCompile Include="Capture\DXGI.cpp" />
<ClCompile Include="Capture\NvFBC.cpp" />
<ClCompile Include="ivshmem.cpp" />
@ -169,7 +169,7 @@
<ClCompile Include="Service.cpp" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="..\vendor\DXGICaptureSample\DXGICaptureSample\DXGIManager.h" />
<ClInclude Include="..\vendor\DXGICapture\DXGIManager.h" />
<ClInclude Include="CaptureFactory.h" />
<ClInclude Include="Capture\DXGI.h" />
<ClInclude Include="Capture\NvFBC.h" />

View File

@ -39,7 +39,7 @@
<ClCompile Include="Capture\DXGI.cpp">
<Filter>Source Files\Capture</Filter>
</ClCompile>
<ClCompile Include="..\vendor\DXGICaptureSample\DXGICaptureSample\DXGIManager.cpp">
<ClCompile Include="..\vendor\DXGICapture\DXGIManager.cpp">
<Filter>Source Files\Capture</Filter>
</ClCompile>
</ItemGroup>
@ -68,7 +68,7 @@
<ClInclude Include="Capture\DXGI.h">
<Filter>Header Files\Capture</Filter>
</ClInclude>
<ClInclude Include="..\vendor\DXGICaptureSample\DXGICaptureSample\DXGIManager.h">
<ClInclude Include="..\vendor\DXGICapture\DXGIManager.h">
<Filter>Header Files\Capture</Filter>
</ClInclude>
</ItemGroup>

753
vendor/DXGICapture/DXGIManager.cpp vendored Normal file
View File

@ -0,0 +1,753 @@
#include "DXGIManager.h"
#include "common\debug.h"
DXGIPointerInfo::DXGIPointerInfo(BYTE* pPointerShape, UINT uiPointerShapeBufSize, DXGI_OUTDUPL_FRAME_INFO fi, DXGI_OUTDUPL_POINTER_SHAPE_INFO psi)
: m_pPointerShape(pPointerShape),
m_uiPointerShapeBufSize(uiPointerShapeBufSize),
m_FI(fi),
m_PSI(psi)
{
}
DXGIPointerInfo::~DXGIPointerInfo()
{
if(m_pPointerShape)
{
delete [] m_pPointerShape;
}
}
BYTE* DXGIPointerInfo::GetBuffer()
{
return m_pPointerShape;
}
UINT DXGIPointerInfo::GetBufferSize()
{
return m_uiPointerShapeBufSize;
}
DXGI_OUTDUPL_FRAME_INFO& DXGIPointerInfo::GetFrameInfo()
{
return m_FI;
}
DXGI_OUTDUPL_POINTER_SHAPE_INFO& DXGIPointerInfo::GetShapeInfo()
{
return m_PSI;
}
DXGIOutputDuplication::DXGIOutputDuplication(IDXGIAdapter1* pAdapter,
ID3D11Device* pD3DDevice,
ID3D11DeviceContext* pD3DDeviceContext,
IDXGIOutput1* pDXGIOutput1,
IDXGIOutputDuplication* pDXGIOutputDuplication)
: m_Adapter(pAdapter),
m_D3DDevice(pD3DDevice),
m_D3DDeviceContext(pD3DDeviceContext),
m_DXGIOutput1(pDXGIOutput1),
m_DXGIOutputDuplication(pDXGIOutputDuplication)
{
}
HRESULT DXGIOutputDuplication::GetDesc(DXGI_OUTPUT_DESC& desc)
{
m_DXGIOutput1->GetDesc(&desc);
return S_OK;
}
HRESULT DXGIOutputDuplication::AcquireNextFrame(IDXGISurface1** pDXGISurface, DXGIPointerInfo*& pDXGIPointer)
{
DXGI_OUTDUPL_FRAME_INFO fi;
CComPtr<IDXGIResource> spDXGIResource;
HRESULT hr = m_DXGIOutputDuplication->AcquireNextFrame(20, &fi, &spDXGIResource);
if(FAILED(hr))
{
DEBUG_INFO("m_DXGIOutputDuplication->AcquireNextFrame failed with hr=0x%08x", hr);
return hr;
}
CComQIPtr<ID3D11Texture2D> spTextureResource = spDXGIResource;
D3D11_TEXTURE2D_DESC desc;
spTextureResource->GetDesc(&desc);
D3D11_TEXTURE2D_DESC texDesc;
ZeroMemory( &texDesc, sizeof(texDesc) );
texDesc.Width = desc.Width;
texDesc.Height = desc.Height;
texDesc.MipLevels = 1;
texDesc.ArraySize = 1;
texDesc.SampleDesc.Count = 1;
texDesc.SampleDesc.Quality = 0;
texDesc.Usage = D3D11_USAGE_STAGING;
texDesc.Format = desc.Format;
texDesc.BindFlags = 0;
texDesc.CPUAccessFlags = D3D11_CPU_ACCESS_READ;
texDesc.MiscFlags = 0;
CComPtr<ID3D11Texture2D> spD3D11Texture2D = NULL;
hr = m_D3DDevice->CreateTexture2D(&texDesc, NULL, &spD3D11Texture2D);
if(FAILED(hr))
return hr;
m_D3DDeviceContext->CopyResource(spD3D11Texture2D, spTextureResource);
CComQIPtr<IDXGISurface1> spDXGISurface = spD3D11Texture2D;
*pDXGISurface = spDXGISurface.Detach();
if (pDXGIPointer)
pDXGIPointer->GetFrameInfo().PointerPosition.Visible = fi.PointerPosition.Visible;
// Updating mouse pointer, if visible
if(fi.PointerPosition.Visible)
{
BYTE* pPointerShape = new BYTE[fi.PointerShapeBufferSize];
DXGI_OUTDUPL_POINTER_SHAPE_INFO psi = {};
UINT uiPointerShapeBufSize = fi.PointerShapeBufferSize;
hr = m_DXGIOutputDuplication->GetFramePointerShape(uiPointerShapeBufSize, pPointerShape, &uiPointerShapeBufSize, &psi);
if(hr == DXGI_ERROR_MORE_DATA)
{
pPointerShape = new BYTE[uiPointerShapeBufSize];
hr = m_DXGIOutputDuplication->GetFramePointerShape(uiPointerShapeBufSize, pPointerShape, &uiPointerShapeBufSize, &psi);
}
if(hr == S_OK)
{
DEBUG_INFO("PointerPosition Visible=%d x=%d y=%d w=%d h=%d type=%d\n", fi.PointerPosition.Visible, fi.PointerPosition.Position.x, fi.PointerPosition.Position.y, psi.Width, psi.Height, psi.Type);
if((psi.Type == DXGI_OUTDUPL_POINTER_SHAPE_TYPE_MONOCHROME ||
psi.Type == DXGI_OUTDUPL_POINTER_SHAPE_TYPE_COLOR ||
psi.Type == DXGI_OUTDUPL_POINTER_SHAPE_TYPE_MASKED_COLOR) &&
psi.Width <= 128 && psi.Height <= 128)
{
// Here we can obtain pointer shape
if(pDXGIPointer)
{
delete pDXGIPointer;
}
pDXGIPointer = new DXGIPointerInfo(pPointerShape, uiPointerShapeBufSize, fi, psi);
pPointerShape = NULL;
}
DXGI_OUTPUT_DESC outDesc;
GetDesc(outDesc);
if(pDXGIPointer)
{
pDXGIPointer->GetFrameInfo().PointerPosition.Position.x = outDesc.DesktopCoordinates.left + fi.PointerPosition.Position.x;
pDXGIPointer->GetFrameInfo().PointerPosition.Position.y = outDesc.DesktopCoordinates.top + fi.PointerPosition.Position.y;
}
}
if(pPointerShape)
{
delete [] pPointerShape;
}
}
return hr;
}
HRESULT DXGIOutputDuplication::ReleaseFrame()
{
m_DXGIOutputDuplication->ReleaseFrame();
return S_OK;
}
bool DXGIOutputDuplication::IsPrimary()
{
DXGI_OUTPUT_DESC outdesc;
m_DXGIOutput1->GetDesc(&outdesc);
MONITORINFO mi;
mi.cbSize = sizeof(MONITORINFO);
GetMonitorInfo(outdesc.Monitor, &mi);
if(mi.dwFlags & MONITORINFOF_PRIMARY)
{
return true;
}
return false;
}
DXGIManager::DXGIManager()
{
m_CaptureSource = CSUndefined;
SetRect(&m_rcCurrentOutput, 0, 0, 0, 0);
m_pBuf = NULL;
m_pDXGIPointer = NULL;
m_bInitialized = false;
}
DXGIManager::~DXGIManager()
{
if(m_pBuf)
{
delete [] m_pBuf;
m_pBuf = NULL;
}
if(m_pDXGIPointer)
{
delete m_pDXGIPointer;
m_pDXGIPointer = NULL;
}
}
HRESULT DXGIManager::SetCaptureSource(CaptureSource cs)
{
m_CaptureSource = cs;
return S_OK;
}
CaptureSource DXGIManager::GetCaptureSource()
{
return m_CaptureSource;
}
HRESULT DXGIManager::Init()
{
if(m_bInitialized)
return S_OK;
HRESULT hr = CreateDXGIFactory1(__uuidof(IDXGIFactory1), (void**)(&m_spDXGIFactory1) );
if( FAILED(hr) )
{
DEBUG_ERROR("Failed to CreateDXGIFactory1 hr=%08x", hr);
return hr;
}
// Getting all adapters
vector<CComPtr<IDXGIAdapter1>> vAdapters;
CComPtr<IDXGIAdapter1> spAdapter;
for(int i=0; m_spDXGIFactory1->EnumAdapters1(i, &spAdapter) != DXGI_ERROR_NOT_FOUND; i++)
{
vAdapters.push_back(spAdapter);
spAdapter.Release();
}
// Iterating over all adapters to get all outputs
for(vector<CComPtr<IDXGIAdapter1>>::iterator AdapterIter = vAdapters.begin();
AdapterIter != vAdapters.end();
AdapterIter++)
{
vector<CComPtr<IDXGIOutput>> vOutputs;
CComPtr<IDXGIOutput> spDXGIOutput;
for(int i=0; (*AdapterIter)->EnumOutputs(i, &spDXGIOutput) != DXGI_ERROR_NOT_FOUND; i++)
{
DXGI_OUTPUT_DESC outputDesc;
spDXGIOutput->GetDesc(&outputDesc);
DEBUG_ERROR("Display output found. DeviceName=%ls AttachedToDesktop=%d Rotation=%d DesktopCoordinates={(%d,%d),(%d,%d)}",
outputDesc.DeviceName,
outputDesc.AttachedToDesktop,
outputDesc.Rotation,
outputDesc.DesktopCoordinates.left,
outputDesc.DesktopCoordinates.top,
outputDesc.DesktopCoordinates.right,
outputDesc.DesktopCoordinates.bottom);
if(outputDesc.AttachedToDesktop)
{
vOutputs.push_back(spDXGIOutput);
}
spDXGIOutput.Release();
}
if(vOutputs.size() == 0)
continue;
// Creating device for each adapter that has the output
CComPtr<ID3D11Device> spD3D11Device;
CComPtr<ID3D11DeviceContext> spD3D11DeviceContext;
D3D_FEATURE_LEVEL fl = D3D_FEATURE_LEVEL_9_1;
hr = D3D11CreateDevice((*AdapterIter), D3D_DRIVER_TYPE_UNKNOWN, NULL, 0, NULL, 0, D3D11_SDK_VERSION, &spD3D11Device, &fl, &spD3D11DeviceContext);
if( FAILED(hr) )
{
DEBUG_ERROR("Failed to create D3D11CreateDevice hr=%08x", hr);
return hr;
}
for(std::vector<CComPtr<IDXGIOutput>>::iterator OutputIter = vOutputs.begin();
OutputIter != vOutputs.end();
OutputIter++)
{
CComQIPtr<IDXGIOutput1> spDXGIOutput1 = *OutputIter;
if (!spDXGIOutput1)
{
DEBUG_ERROR("spDXGIOutput1 is NULL");
continue;
}
CComQIPtr<IDXGIDevice1> spDXGIDevice = spD3D11Device;
if (!spDXGIDevice)
{
DEBUG_ERROR("spDXGIDevice is NULL");
continue;
}
CComPtr<IDXGIOutputDuplication> spDXGIOutputDuplication;
hr = spDXGIOutput1->DuplicateOutput(spDXGIDevice, &spDXGIOutputDuplication);
if (FAILED(hr))
{
DEBUG_ERROR("Failed to duplicate output hr=%08x", hr);
continue;
}
m_vOutputs.push_back(
DXGIOutputDuplication((*AdapterIter),
spD3D11Device,
spD3D11DeviceContext,
spDXGIOutput1,
spDXGIOutputDuplication));
}
}
hr = m_spWICFactory.CoCreateInstance(CLSID_WICImagingFactory);
if( FAILED(hr) )
{
DEBUG_ERROR("Failed to create WICImagingFactory hr=%08x", hr);
return hr;
}
m_bInitialized = true;
return S_OK;
}
HRESULT DXGIManager::GetOutputRect(RECT& rc)
{
// Nulling rc just in case...
SetRect(&rc, 0, 0, 0, 0);
HRESULT hr = Init();
if(hr != S_OK)
return hr;
vector<DXGIOutputDuplication> vOutputs = GetOutputDuplication();
RECT rcShare;
SetRect(&rcShare, 0, 0, 0, 0);
for(vector<DXGIOutputDuplication>::iterator iter = vOutputs.begin();
iter != vOutputs.end();
iter++)
{
DXGIOutputDuplication& out = *iter;
DXGI_OUTPUT_DESC outDesc;
out.GetDesc(outDesc);
RECT rcOutCoords = outDesc.DesktopCoordinates;
UnionRect(&rcShare, &rcShare, &rcOutCoords);
}
CopyRect(&rc, &rcShare);
return S_OK;
}
HRESULT DXGIManager::GetOutputBits(BYTE* pBits, RECT& rcDest)
{
HRESULT hr = S_OK;
DWORD dwDestWidth = rcDest.right - rcDest.left;
DWORD dwDestHeight = rcDest.bottom - rcDest.top;
RECT rcOutput;
hr = GetOutputRect(rcOutput);
if( FAILED(hr) )
return hr;
DWORD dwOutputWidth = rcOutput.right - rcOutput.left;
DWORD dwOutputHeight = rcOutput.bottom - rcOutput.top;
BYTE* pBuf = NULL;
if(rcOutput.right > (LONG)dwDestWidth || rcOutput.bottom > (LONG)dwDestHeight)
{
// Output is larger than pBits dimensions
if(!m_pBuf || !EqualRect(&m_rcCurrentOutput, &rcOutput))
{
DWORD dwBufSize = dwOutputWidth*dwOutputHeight*4;
if(m_pBuf)
{
delete [] m_pBuf;
m_pBuf = NULL;
}
m_pBuf = new BYTE[dwBufSize];
CopyRect(&m_rcCurrentOutput, &rcOutput);
}
pBuf = m_pBuf;
}
else
{
// Output is smaller than pBits dimensions
pBuf = pBits;
dwOutputWidth = dwDestWidth;
dwOutputHeight = dwDestHeight;
}
vector<DXGIOutputDuplication> vOutputs = GetOutputDuplication();
for(vector<DXGIOutputDuplication>::iterator iter = vOutputs.begin();
iter != vOutputs.end();
iter++)
{
DXGIOutputDuplication& out = *iter;
DXGI_OUTPUT_DESC outDesc;
out.GetDesc(outDesc);
RECT rcOutCoords = outDesc.DesktopCoordinates;
CComPtr<IDXGISurface1> spDXGISurface1;
hr = out.AcquireNextFrame(&spDXGISurface1, m_pDXGIPointer);
if( FAILED(hr) )
break;
DXGI_MAPPED_RECT map;
spDXGISurface1->Map(&map, DXGI_MAP_READ);
RECT rcDesktop = outDesc.DesktopCoordinates;
DWORD dwWidth = rcDesktop.right - rcDesktop.left;
DWORD dwHeight = rcDesktop.bottom - rcDesktop.top;
OffsetRect(&rcDesktop, -rcOutput.left, -rcOutput.top);
DWORD dwMapPitchPixels = map.Pitch/4;
switch(outDesc.Rotation)
{
case DXGI_MODE_ROTATION_IDENTITY:
{
// Just copying
DWORD dwStripe = dwWidth*4;
for(unsigned int i=0; i<dwHeight; i++)
{
memcpy_s(pBuf + (rcDesktop.left + (i + rcDesktop.top)*dwOutputWidth)*4, dwStripe, map.pBits + i*map.Pitch, dwStripe);
}
}
break;
case DXGI_MODE_ROTATION_ROTATE90:
{
// Rotating at 90 degrees
DWORD* pSrc = (DWORD*)map.pBits;
DWORD* pDst = (DWORD*)pBuf;
for(unsigned int j=0; j<dwHeight; j++)
{
for(unsigned int i=0; i<dwWidth; i++)
{
*(pDst + (rcDesktop.left + (j + rcDesktop.top)*dwOutputWidth) + i) = *(pSrc + j + dwMapPitchPixels*(dwWidth - i - 1));
}
}
}
break;
case DXGI_MODE_ROTATION_ROTATE180:
{
// Rotating at 180 degrees
DWORD* pSrc = (DWORD*)map.pBits;
DWORD* pDst = (DWORD*)pBuf;
for(unsigned int j=0; j<dwHeight; j++)
{
for(unsigned int i=0; i<dwWidth; i++)
{
*(pDst + (rcDesktop.left + (j + rcDesktop.top)*dwOutputWidth) + i) = *(pSrc + (dwWidth - i - 1) + dwMapPitchPixels*(dwHeight - j - 1));
}
}
}
break;
case DXGI_MODE_ROTATION_ROTATE270:
{
// Rotating at 270 degrees
DWORD* pSrc = (DWORD*)map.pBits;
DWORD* pDst = (DWORD*)pBuf;
for(unsigned int j=0; j<dwHeight; j++)
{
for(unsigned int i=0; i<dwWidth; i++)
{
*(pDst + (rcDesktop.left + (j + rcDesktop.top)*dwOutputWidth) + i) = *(pSrc + (dwHeight - j - 1) + dwMapPitchPixels*i);
}
}
}
break;
}
spDXGISurface1->Unmap();
out.ReleaseFrame();
}
if(FAILED(hr))
return hr;
// We have the pBuf filled with current desktop/monitor image.
if(pBuf != pBits)
{
DrawMousePointer(pBuf, rcOutput, rcOutput);
// pBuf contains the image that should be resized
CComPtr<IWICBitmap> spBitmap = NULL;
hr = m_spWICFactory->CreateBitmapFromMemory(dwOutputWidth, dwOutputHeight, GUID_WICPixelFormat32bppBGRA, dwOutputWidth*4, dwOutputWidth*dwOutputHeight*4, (BYTE*)pBuf, &spBitmap);
if( FAILED(hr) )
return hr;
CComPtr<IWICBitmapScaler> spBitmapScaler = NULL;
hr = m_spWICFactory->CreateBitmapScaler(&spBitmapScaler);
if( FAILED(hr) )
return hr;
dwOutputWidth = rcOutput.right - rcOutput.left;
dwOutputHeight = rcOutput.bottom - rcOutput.top;
double aspect = (double)dwOutputWidth/(double)dwOutputHeight;
DWORD scaledWidth = dwDestWidth;
DWORD scaledHeight = dwDestHeight;
if(aspect > 1)
{
scaledWidth = dwDestWidth;
scaledHeight = (DWORD)(dwDestWidth/aspect);
}
else
{
scaledWidth = (DWORD)(aspect*dwDestHeight);
scaledHeight = dwDestHeight;
}
spBitmapScaler->Initialize(
spBitmap, scaledWidth, scaledHeight, WICBitmapInterpolationModeNearestNeighbor);
spBitmapScaler->CopyPixels(NULL, scaledWidth*4, dwDestWidth*dwDestHeight*4, pBits);
}
else
DrawMousePointer(pBuf, rcOutput, rcDest);
return hr;
}
void DXGIManager::DrawMousePointer(BYTE* pDesktopBits, RECT rcDesktop, RECT rcDest)
{
const DXGI_OUTDUPL_FRAME_INFO frameInfo = m_pDXGIPointer->GetFrameInfo();
if(!m_pDXGIPointer || !frameInfo.PointerPosition.Visible)
return;
const DWORD dwDesktopWidth = rcDesktop.right - rcDesktop.left;
const DWORD dwDesktopHeight = rcDesktop.bottom - rcDesktop.top;
const DWORD dwDestWidth = rcDest.right - rcDest.left;
const DWORD dwDestHeight = rcDest.bottom - rcDest.top;
const DXGI_OUTDUPL_POINTER_SHAPE_INFO shapeInfo = m_pDXGIPointer->GetShapeInfo();
const int PtrX = frameInfo.PointerPosition.Position.x - rcDesktop.left;
const int PtrY = frameInfo.PointerPosition.Position.y - rcDesktop.top;
BYTE *PtrBuf = m_pDXGIPointer->GetBuffer();
switch(shapeInfo.Type)
{
case DXGI_OUTDUPL_POINTER_SHAPE_TYPE_COLOR:
{
// alpha blend the cursor
const int maxX = min(shapeInfo.Width , dwDesktopWidth - PtrX);
const int maxY = min(shapeInfo.Height, dwDesktopHeight - PtrY);
for(int y = 0; y < maxY; ++y)
{
for (int x = 0; x < maxX; ++x)
{
BYTE *srcPix = &PtrBuf[y * shapeInfo.Pitch + x * 4];
BYTE *dstPix = &pDesktopBits[((PtrY + y) * dwDesktopWidth * 4) + (PtrX + x) * 4];
// if fully transparent just continue
if (srcPix[3] == 0x00)
continue;
// if fully opaque just copy the pixel
if (srcPix[3] == 0xff)
{
dstPix[0] = srcPix[0];
dstPix[1] = srcPix[1];
dstPix[2] = srcPix[2];
continue;
}
//TODO: optimize this to use integer math
float src[3];
float dst[3];
float alpha;
static const float conv = 1.0f / 255.0f;
// convert to float
src[0] = (float)srcPix[0] * conv;
src[1] = (float)srcPix[1] * conv;
src[2] = (float)srcPix[2] * conv;
dst[0] = (float)dstPix[0] * conv;
dst[1] = (float)dstPix[1] * conv;
dst[2] = (float)dstPix[2] * conv;
alpha = (float)srcPix[3] * conv;
// blend and convert back to 8bit
dstPix[0] = (BYTE)(max(0.0f, min(1.0f, alpha * src[0] + dst[0] * (1.0f - alpha))) * 255.0f);
dstPix[1] = (BYTE)(max(0.0f, min(1.0f, alpha * src[1] + dst[1] * (1.0f - alpha))) * 255.0f);
dstPix[2] = (BYTE)(max(0.0f, min(1.0f, alpha * src[2] + dst[2] * (1.0f - alpha))) * 255.0f);
}
}
break;
}
break;
case DXGI_OUTDUPL_POINTER_SHAPE_TYPE_MONOCHROME:
case DXGI_OUTDUPL_POINTER_SHAPE_TYPE_MASKED_COLOR:
{
RECT rcPointer;
if(shapeInfo.Type == DXGI_OUTDUPL_POINTER_SHAPE_TYPE_MONOCHROME)
{
SetRect(&rcPointer, PtrX, PtrY, PtrX + shapeInfo.Width, PtrY + shapeInfo.Height/2);
}
else
{
SetRect(&rcPointer, PtrX, PtrY, PtrX + shapeInfo.Width, PtrY + shapeInfo.Height);
}
RECT rcDesktopPointer;
IntersectRect(&rcDesktopPointer, &rcPointer, &rcDesktop);
CopyRect(&rcPointer, &rcDesktopPointer);
OffsetRect(&rcPointer, -PtrX, -PtrY);
BYTE* pShapeBuffer = m_pDXGIPointer->GetBuffer();
UINT* pDesktopBits32 = (UINT*)pDesktopBits;
if(shapeInfo.Type == DXGI_OUTDUPL_POINTER_SHAPE_TYPE_MONOCHROME)
{
for(int j = rcPointer.top, jDP = rcDesktopPointer.top;
j<rcPointer.bottom && jDP<rcDesktopPointer.bottom;
j++, jDP++)
{
for(int i = rcPointer.left, iDP = rcDesktopPointer.left;
i<rcPointer.right && iDP<rcDesktopPointer.right;
i++, iDP++)
{
BYTE Mask = 0x80 >> (i % 8);
BYTE AndMask = pShapeBuffer[i/8 + (shapeInfo.Pitch)*j] & Mask;
BYTE XorMask = pShapeBuffer[i/8 + (shapeInfo.Pitch)*(j + shapeInfo.Height / 2)] & Mask;
UINT AndMask32 = (AndMask) ? 0xFFFFFFFF : 0xFF000000;
UINT XorMask32 = (XorMask) ? 0x00FFFFFF : 0x00000000;
pDesktopBits32[jDP*dwDestWidth + iDP] = (pDesktopBits32[jDP*dwDestWidth + iDP] & AndMask32) ^ XorMask32;
}
}
}
else
{
UINT* pShapeBuffer32 = (UINT*)pShapeBuffer;
for(int j = rcPointer.top, jDP = rcDesktopPointer.top;
j<rcPointer.bottom && jDP<rcDesktopPointer.bottom;
j++, jDP++)
{
for(int i = rcPointer.left, iDP = rcDesktopPointer.left;
i<rcPointer.right && iDP<rcDesktopPointer.right;
i++, iDP++)
{
// Set up mask
UINT MaskVal = 0xFF000000 & pShapeBuffer32[i + (shapeInfo.Pitch/4)*j];
if (MaskVal)
{
// Mask was 0xFF
pDesktopBits32[jDP*dwDestWidth + iDP] = (pDesktopBits32[jDP*dwDestWidth + iDP] ^ pShapeBuffer32[i + (shapeInfo.Pitch/4)*j]) | 0xFF000000;
}
else
{
// Mask was 0x00 - replacing pixel
pDesktopBits32[jDP*dwDestWidth + iDP] = pShapeBuffer32[i + (shapeInfo.Pitch/4)*j];
}
}
}
}
}
break;
}
}
vector<DXGIOutputDuplication> DXGIManager::GetOutputDuplication()
{
vector<DXGIOutputDuplication> outputs;
switch(m_CaptureSource)
{
case CSMonitor1:
{
// Return the one with IsPrimary
for(vector<DXGIOutputDuplication>::iterator iter = m_vOutputs.begin();
iter != m_vOutputs.end();
iter++)
{
DXGIOutputDuplication& out = *iter;
if(out.IsPrimary())
{
outputs.push_back(out);
break;
}
}
}
break;
case CSMonitor2:
{
// Return the first with !IsPrimary
for(vector<DXGIOutputDuplication>::iterator iter = m_vOutputs.begin();
iter != m_vOutputs.end();
iter++)
{
DXGIOutputDuplication& out = *iter;
if(!out.IsPrimary())
{
outputs.push_back(out);
break;
}
}
}
break;
case CSDesktop:
{
// Return all outputs
for(vector<DXGIOutputDuplication>::iterator iter = m_vOutputs.begin();
iter != m_vOutputs.end();
iter++)
{
DXGIOutputDuplication& out = *iter;
outputs.push_back(out);
}
}
break;
}
return outputs;
}
BOOL CALLBACK MonitorEnumProc(HMONITOR hMonitor, HDC hdcMonitor, LPRECT lprcMonitor, LPARAM dwData)
{
int *Count = (int*)dwData;
(*Count)++;
return TRUE;
}
int DXGIManager::GetMonitorCount()
{
int Count = 0;
if (EnumDisplayMonitors(NULL, NULL, MonitorEnumProc, (LPARAM)&Count))
return Count;
return -1;
}

89
vendor/DXGICapture/DXGIManager.h vendored Normal file
View File

@ -0,0 +1,89 @@
#pragma once
#include <windows.h>
#include <atlbase.h>
#include <DXGITYPE.h>
#include <DXGI1_2.h>
#include <d3d11.h>
#include <Wincodec.h>
#include <vector>
using namespace std;
class DXGIPointerInfo;
enum CaptureSource
{
CSUndefined,
CSMonitor1,
CSMonitor2,
CSDesktop
};
class DXGIPointerInfo
{
public:
DXGIPointerInfo(BYTE* pPointerShape, UINT uiPointerShapeBufSize, DXGI_OUTDUPL_FRAME_INFO fi, DXGI_OUTDUPL_POINTER_SHAPE_INFO psi);
~DXGIPointerInfo();
BYTE* GetBuffer();
UINT GetBufferSize();
DXGI_OUTDUPL_FRAME_INFO& GetFrameInfo();
DXGI_OUTDUPL_POINTER_SHAPE_INFO& GetShapeInfo();
private:
BYTE* m_pPointerShape;
UINT m_uiPointerShapeBufSize;
DXGI_OUTDUPL_POINTER_SHAPE_INFO m_PSI;
DXGI_OUTDUPL_FRAME_INFO m_FI;
};
class DXGIOutputDuplication
{
public:
DXGIOutputDuplication(IDXGIAdapter1* pAdapter,
ID3D11Device* pD3DDevice,
ID3D11DeviceContext* pD3DDeviceContext,
IDXGIOutput1* pDXGIOutput1,
IDXGIOutputDuplication* pDXGIOutputDuplication);
HRESULT GetDesc(DXGI_OUTPUT_DESC& desc);
HRESULT AcquireNextFrame(IDXGISurface1** pD3D11Texture2D, DXGIPointerInfo*& pDXGIPointer);
HRESULT ReleaseFrame();
bool IsPrimary();
private:
CComPtr<IDXGIAdapter1> m_Adapter;
CComPtr<ID3D11Device> m_D3DDevice;
CComPtr<ID3D11DeviceContext> m_D3DDeviceContext;
CComPtr<IDXGIOutput1> m_DXGIOutput1;
CComPtr<IDXGIOutputDuplication> m_DXGIOutputDuplication;
};
class DXGIManager
{
public:
DXGIManager();
~DXGIManager();
HRESULT SetCaptureSource(CaptureSource type);
CaptureSource GetCaptureSource();
HRESULT GetOutputRect(RECT& rc);
HRESULT GetOutputBits(BYTE* pBits, RECT& rcDest);
private:
HRESULT Init();
int GetMonitorCount();
vector<DXGIOutputDuplication> GetOutputDuplication();
void DrawMousePointer(BYTE* pDesktopBits, RECT rcDesktop, RECT rcDest);
private:
CComPtr<IDXGIFactory1> m_spDXGIFactory1;
vector<DXGIOutputDuplication> m_vOutputs;
bool m_bInitialized;
CaptureSource m_CaptureSource;
RECT m_rcCurrentOutput;
BYTE* m_pBuf;
CComPtr<IWICImagingFactory> m_spWICFactory;
ULONG_PTR m_gdiplusToken;
DXGIPointerInfo* m_pDXGIPointer;
};

5
vendor/DXGICapture/README vendored Normal file
View File

@ -0,0 +1,5 @@
code in this directory is temporary and requires a re-write, it is horrible and
to be deprecated ASAP.
The reason for keeping a local copy is because I have fixed some stupid bugs
and implemented proper alpha blending for the cursor.

@ -1 +0,0 @@
Subproject commit 9e304f24b5b11bbd1c61a9c1c528693cd2843c5d