diff --git a/.gitignore b/.gitignore index e373c50..8c6314a 100644 --- a/.gitignore +++ b/.gitignore @@ -261,3 +261,4 @@ __pycache__/ *.pyc Win32CaptureSample/output.png +/Win32CaptureSample/output.jxr diff --git a/Win32CaptureSample/App.cpp b/Win32CaptureSample/App.cpp index b552fcb..7832f8a 100644 --- a/Win32CaptureSample/App.cpp +++ b/Win32CaptureSample/App.cpp @@ -1,6 +1,5 @@ #include "pch.h" #include "App.h" -#include "SimpleCapture.h" #include "CaptureSnapshot.h" using namespace winrt; @@ -10,7 +9,9 @@ using namespace Windows::System; using namespace Windows::Foundation; using namespace Windows::UI; using namespace Windows::UI::Composition; +using namespace Windows::UI::Popups; using namespace Windows::Graphics::Capture; +using namespace Windows::Graphics::DirectX; App::App(ContainerVisual root, GraphicsCapturePicker capturePicker, FileSavePicker savePicker) { @@ -44,9 +45,7 @@ App::App(ContainerVisual root, GraphicsCapturePicker capturePicker, FileSavePick auto dxgiDevice = d3dDevice.as(); m_device = CreateDirect3DDevice(dxgiDevice.get()); - m_d2dFactory = CreateD2DFactory(); - m_d2dDevice = CreateD2DDevice(m_d2dFactory, d3dDevice); - check_hresult(m_d2dDevice->CreateDeviceContext(D2D1_DEVICE_CONTEXT_OPTIONS_NONE, m_d2dContext.put())); + m_encoder = std::make_unique(m_device); } GraphicsCaptureItem App::StartCaptureFromWindowHandle(HWND hwnd) @@ -100,55 +99,50 @@ IAsyncOperation App::TakeSnapshotAsync() m_savePicker.DefaultFileExtension(L".png"); m_savePicker.FileTypeChoices().Clear(); m_savePicker.FileTypeChoices().Insert(L"PNG image", single_threaded_vector({ L".png" })); + m_savePicker.FileTypeChoices().Insert(L"JPG image", single_threaded_vector({ L".jpg" })); + m_savePicker.FileTypeChoices().Insert(L"JXR image", single_threaded_vector({ L".jxr" })); auto file = co_await m_savePicker.PickSaveFileAsync(); if (file == nullptr) { co_return nullptr; } + // Decide on the pixel format depending on the image type + auto fileExtension = file.FileType(); + SimpleImageEncoder::SupportedFormats fileFormat; + DirectXPixelFormat pixelFormat; + if (fileExtension == L".png") + { + fileFormat = SimpleImageEncoder::SupportedFormats::Png; + pixelFormat = DirectXPixelFormat::B8G8R8A8UIntNormalized; + } + else if (fileExtension == L".jpg" || fileExtension == L".jpeg") + { + fileFormat = SimpleImageEncoder::SupportedFormats::Jpg; + pixelFormat = DirectXPixelFormat::B8G8R8A8UIntNormalized; + } + else if (fileExtension == L".jxr") + { + fileFormat = SimpleImageEncoder::SupportedFormats::Jxr; + pixelFormat = DirectXPixelFormat::R16G16B16A16Float; + } + else + { + // Unsupported + auto dialog = MessageDialog(L"Unsupported file format!"); + + co_await dialog.ShowAsync(); + co_return nullptr; + } + // Get the file stream - auto randomAccessStream = co_await file.OpenAsync(FileAccessMode::ReadWrite); - auto stream = CreateStreamFromRandomAccessStream(randomAccessStream); + auto stream = co_await file.OpenAsync(FileAccessMode::ReadWrite); // Take the snapshot - auto frame = co_await CaptureSnapshot::TakeAsync(m_device, item); - auto frameTexture = GetDXGIInterfaceFromObject(frame); - D3D11_TEXTURE2D_DESC textureDesc = {}; - frameTexture->GetDesc(&textureDesc); - auto dxgiFrameTexture = frameTexture.as(); - - // Get a D2D bitmap for our snapshot - // TODO: Since this sample doesn't use D2D any other way, it may be better to map - // the pixels manually and hand them to WIC. However, using d2d is easier for now. - com_ptr d2dBitmap; - check_hresult(m_d2dContext->CreateBitmapFromDxgiSurface(dxgiFrameTexture.get(), nullptr, d2dBitmap.put())); - - // Encode the snapshot - // TODO: dpi? - auto dpi = 96.0f; - WICImageParameters params = {}; - params.PixelFormat.format = DXGI_FORMAT_B8G8R8A8_UNORM; - params.PixelFormat.alphaMode = D2D1_ALPHA_MODE_PREMULTIPLIED; - params.DpiX = dpi; - params.DpiY = dpi; - params.PixelWidth = textureDesc.Width; - params.PixelHeight = textureDesc.Height; - - auto wicFactory = CreateWICFactory(); - com_ptr encoder; - check_hresult(wicFactory->CreateEncoder(GUID_ContainerFormatPng, nullptr, encoder.put())); - check_hresult(encoder->Initialize(stream.get(), WICBitmapEncoderNoCache)); - - com_ptr wicFrame; - com_ptr frameProperties; - check_hresult(encoder->CreateNewFrame(wicFrame.put(), frameProperties.put())); - check_hresult(wicFrame->Initialize(frameProperties.get())); - - com_ptr imageEncoder; - check_hresult(wicFactory->CreateImageEncoder(m_d2dDevice.get(), imageEncoder.put())); - check_hresult(imageEncoder->WriteFrame(d2dBitmap.get(), wicFrame.get(), ¶ms)); - check_hresult(wicFrame->Commit()); - check_hresult(encoder->Commit()); + auto frame = co_await CaptureSnapshot::TakeAsync(m_device, item, pixelFormat); + + // Encode the image + m_encoder->EncodeImage(frame, stream, fileFormat); co_return file; } @@ -163,7 +157,7 @@ void App::SnapshotCurrentCapture() void App::StartCaptureFromItem(GraphicsCaptureItem item) { - m_capture = std::make_unique(m_device, item); + m_capture = std::make_unique(m_device, item, m_pixelFormat); auto surface = m_capture->CreateSurface(m_compositor); m_brush.Surface(surface); @@ -179,4 +173,15 @@ void App::StopCapture() m_capture = nullptr; m_brush.Surface(nullptr); } +} + +void App::PixelFormat(DirectXPixelFormat pixelFormat) +{ + m_pixelFormat = pixelFormat; + if (m_capture) + { + auto item = m_capture->CaptureItem(); + StopCapture(); + StartCaptureFromItem(item); + } } \ No newline at end of file diff --git a/Win32CaptureSample/App.h b/Win32CaptureSample/App.h index 5184401..fd92fa4 100644 --- a/Win32CaptureSample/App.h +++ b/Win32CaptureSample/App.h @@ -1,5 +1,6 @@ #pragma once #include "SimpleCapture.h" +#include "SimpleImageEncoder.h" class App { @@ -14,6 +15,8 @@ class App winrt::Windows::Foundation::IAsyncOperation StartCaptureWithPickerAsync(); winrt::Windows::Foundation::IAsyncOperation TakeSnapshotAsync(); void SnapshotCurrentCapture(); + winrt::Windows::Graphics::DirectX::DirectXPixelFormat PixelFormat() { return m_pixelFormat; } + void PixelFormat(winrt::Windows::Graphics::DirectX::DirectXPixelFormat pixelFormat); void StopCapture(); @@ -32,8 +35,7 @@ class App winrt::Windows::Graphics::DirectX::Direct3D11::IDirect3DDevice m_device{ nullptr }; std::unique_ptr m_capture{ nullptr }; + winrt::Windows::Graphics::DirectX::DirectXPixelFormat m_pixelFormat = winrt::Windows::Graphics::DirectX::DirectXPixelFormat::B8G8R8A8UIntNormalized; - winrt::com_ptr m_d2dFactory; - winrt::com_ptr m_d2dDevice; - winrt::com_ptr m_d2dContext; + std::unique_ptr m_encoder{ nullptr }; }; \ No newline at end of file diff --git a/Win32CaptureSample/CaptureSnapshot.cpp b/Win32CaptureSample/CaptureSnapshot.cpp index efc380f..b4bb174 100644 --- a/Win32CaptureSample/CaptureSnapshot.cpp +++ b/Win32CaptureSample/CaptureSnapshot.cpp @@ -15,7 +15,7 @@ using namespace Windows::UI; using namespace Windows::UI::Composition; IAsyncOperation -CaptureSnapshot::TakeAsync(IDirect3DDevice const& device, GraphicsCaptureItem const& item) +CaptureSnapshot::TakeAsync(IDirect3DDevice const& device, GraphicsCaptureItem const& item, DirectXPixelFormat const& pixelFormat) { auto d3dDevice = GetDXGIInterfaceFromObject(device); com_ptr d3dContext; @@ -27,7 +27,7 @@ CaptureSnapshot::TakeAsync(IDirect3DDevice const& device, GraphicsCaptureItem co // DispatcherQueue requirement. auto framePool = Direct3D11CaptureFramePool::CreateFreeThreaded( device, - DirectXPixelFormat::B8G8R8A8UIntNormalized, + pixelFormat, 1, item.Size()); auto session = framePool.CreateCaptureSession(item); diff --git a/Win32CaptureSample/CaptureSnapshot.h b/Win32CaptureSample/CaptureSnapshot.h index 9e18190..80d7d9d 100644 --- a/Win32CaptureSample/CaptureSnapshot.h +++ b/Win32CaptureSample/CaptureSnapshot.h @@ -6,7 +6,8 @@ class CaptureSnapshot static winrt::Windows::Foundation::IAsyncOperation TakeAsync( winrt::Windows::Graphics::DirectX::Direct3D11::IDirect3DDevice const& device, - winrt::Windows::Graphics::Capture::GraphicsCaptureItem const& item); + winrt::Windows::Graphics::Capture::GraphicsCaptureItem const& item, + winrt::Windows::Graphics::DirectX::DirectXPixelFormat const& format = winrt::Windows::Graphics::DirectX::DirectXPixelFormat::B8G8R8A8UIntNormalized); private: CaptureSnapshot() = delete; diff --git a/Win32CaptureSample/SampleWindow.cpp b/Win32CaptureSample/SampleWindow.cpp index d58a4ed..6ac12e4 100644 --- a/Win32CaptureSample/SampleWindow.cpp +++ b/Win32CaptureSample/SampleWindow.cpp @@ -11,6 +11,7 @@ using namespace Windows::System; using namespace Windows::UI; using namespace Windows::UI::Composition; using namespace Windows::UI::Composition::Desktop; +using namespace Windows::Graphics::DirectX; const std::wstring SampleWindow::ClassName = L"Win32CaptureSample"; @@ -42,6 +43,11 @@ SampleWindow::SampleWindow(HINSTANCE instance, int cmdShow, std::shared_ptr m_app = app; m_windows = std::make_unique(); m_monitors = std::make_unique(); + m_pixelFormats = + { + { L"B8G8R8A8UIntNormalized", DirectXPixelFormat::B8G8R8A8UIntNormalized }, + { L"R16G16B16A16Float", DirectXPixelFormat::R16G16B16A16Float } + }; CreateControls(instance); } @@ -84,6 +90,11 @@ LRESULT SampleWindow::MessageHandler(UINT const message, WPARAM const wparam, LP SetSubTitle(std::wstring(item.DisplayName())); SendMessageW(m_windowComboBoxHwnd, CB_SETCURSEL, -1, 0); } + else if (hwnd == m_pixelFormatComboBoxHwnd) + { + auto pixelFormatData = m_pixelFormats[index]; + m_app->PixelFormat(pixelFormatData.PixelFormat); + } } break; case BN_CLICKED: @@ -193,12 +204,28 @@ void SampleWindow::CreateControls(HINSTANCE instance) 10, 200, 200, 30, m_window, nullptr, instance, nullptr); WINRT_VERIFY(snapshotButtonHwnd); + // Create pixel format combo box + HWND pixelFormatComboBox = CreateWindowW(WC_COMBOBOX, L"", + CBS_DROPDOWNLIST | CBS_HASSTRINGS | WS_VSCROLL | WS_CHILD | WS_OVERLAPPED | WS_VISIBLE, + 10, 240, 200, 200, m_window, nullptr, instance, nullptr); + WINRT_VERIFY(pixelFormatComboBox); + + // Populate pixel format combo box + for (auto& pixelFormat : m_pixelFormats) + { + SendMessageW(pixelFormatComboBox, CB_ADDSTRING, 0, (LPARAM)pixelFormat.Name.c_str()); + } + + // The default pixel format is BGRA8 + SendMessageW(pixelFormatComboBox, CB_SETCURSEL, 0, 0); + m_windowComboBoxHwnd = windowComboBoxHwnd; m_monitorComboBoxHwnd = monitorComboBoxHwnd; m_pickerButtonHwnd = pickerButtonHwnd; m_stopButtonHwnd = stopButtonHwnd; m_currentSnapshotHwnd = currentSnapshotButtonHwnd; m_snapshotButtonHwnd = snapshotButtonHwnd; + m_pixelFormatComboBoxHwnd = pixelFormatComboBox; } void SampleWindow::SetSubTitle(std::wstring const& text) diff --git a/Win32CaptureSample/SampleWindow.h b/Win32CaptureSample/SampleWindow.h index 0c8189d..d4b45b0 100644 --- a/Win32CaptureSample/SampleWindow.h +++ b/Win32CaptureSample/SampleWindow.h @@ -34,6 +34,12 @@ struct SampleWindow : DesktopWindow void StopCapture(); void OnCaptureItemClosed(winrt::Windows::Graphics::Capture::GraphicsCaptureItem const&, winrt::Windows::Foundation::IInspectable const&); + struct PixelFormatData + { + std::wstring Name; + winrt::Windows::Graphics::DirectX::DirectXPixelFormat PixelFormat; + }; + private: HWND m_windowComboBoxHwnd = nullptr; HWND m_monitorComboBoxHwnd = nullptr; @@ -41,8 +47,10 @@ struct SampleWindow : DesktopWindow HWND m_stopButtonHwnd = nullptr; HWND m_currentSnapshotHwnd = nullptr; HWND m_snapshotButtonHwnd = nullptr; + HWND m_pixelFormatComboBoxHwnd = nullptr; std::unique_ptr m_windows; std::unique_ptr m_monitors; + std::vector m_pixelFormats; std::shared_ptr m_app; winrt::Windows::Graphics::Capture::GraphicsCaptureItem::Closed_revoker m_itemClosedRevoker; }; \ No newline at end of file diff --git a/Win32CaptureSample/SimpleCapture.cpp b/Win32CaptureSample/SimpleCapture.cpp index f546020..06ba4a4 100644 --- a/Win32CaptureSample/SimpleCapture.cpp +++ b/Win32CaptureSample/SimpleCapture.cpp @@ -15,23 +15,24 @@ namespace winrt using namespace Windows::UI::Composition; } -SimpleCapture::SimpleCapture(winrt::IDirect3DDevice const& device, winrt::GraphicsCaptureItem const& item) +SimpleCapture::SimpleCapture(winrt::IDirect3DDevice const& device, winrt::GraphicsCaptureItem const& item, winrt::DirectXPixelFormat pixelFormat) { m_item = item; m_device = device; + m_pixelFormat = pixelFormat; auto d3dDevice = GetDXGIInterfaceFromObject(m_device); d3dDevice->GetImmediateContext(m_d3dContext.put()); m_swapChain = CreateDXGISwapChain(d3dDevice, static_cast(m_item.Size().Width), static_cast(m_item.Size().Height), - DXGI_FORMAT_B8G8R8A8_UNORM, 2); + static_cast(m_pixelFormat), 2); // Creating our frame pool with 'Create' instead of 'CreateFreeThreaded' // means that the frame pool's FrameArrived event is called on the thread // the frame pool was created on. This also means that the creating thread // must have a DispatcherQueue. If you use this method, it's best not to do // it on the UI thread. - m_framePool = winrt::Direct3D11CaptureFramePool::Create(m_device, winrt::DirectXPixelFormat::B8G8R8A8UIntNormalized, 2, m_item.Size()); + m_framePool = winrt::Direct3D11CaptureFramePool::Create(m_device, m_pixelFormat, 2, m_item.Size()); m_session = m_framePool.CreateCaptureSession(m_item); m_lastSize = m_item.Size(); m_framePool.FrameArrived({ this, &SimpleCapture::OnFrameArrived }); @@ -74,8 +75,8 @@ bool SimpleCapture::TryResizeSwapChain(winrt::Direct3D11CaptureFrame const& fram { // The thing we have been capturing has changed size, resize the swap chain to match. m_lastSize = contentSize; - m_swapChain->ResizeBuffers(2, static_cast(m_lastSize.Width), static_cast(m_lastSize.Height), - DXGI_FORMAT_B8G8R8A8_UNORM, 0); + winrt::check_hresult(m_swapChain->ResizeBuffers(2, static_cast(m_lastSize.Width), static_cast(m_lastSize.Height), + static_cast(m_pixelFormat), 0)); return true; } return false; @@ -107,7 +108,7 @@ void SimpleCapture::OnFrameArrived(winrt::Direct3D11CaptureFramePool const& send if (swapChainResizedToFrame) { - m_framePool.Recreate(m_device, winrt::DirectXPixelFormat::B8G8R8A8UIntNormalized, 2, m_lastSize); + m_framePool.Recreate(m_device, m_pixelFormat, 2, m_lastSize); } } @@ -117,6 +118,14 @@ void SimpleCapture::TakeSnapshot(winrt::com_ptr const& frame) winrt::check_hresult(DirectX::CaptureTexture(GetDXGIInterfaceFromObject(m_device).get(), m_d3dContext.get(), frame.get(), im)); const auto& realImage = *im.GetImage(0, 0, 0); - winrt::check_hresult(DirectX::SaveToWICFile(realImage, DirectX::WIC_FLAGS_NONE, - GUID_ContainerFormatPng, L"output.png")); + if (m_pixelFormat == winrt::DirectXPixelFormat::R16G16B16A16Float) + { + winrt::check_hresult(DirectX::SaveToWICFile(realImage, DirectX::WIC_FLAGS_NONE, + GUID_ContainerFormatWmp, L"output.jxr")); + } + else // BGRA8 + { + winrt::check_hresult(DirectX::SaveToWICFile(realImage, DirectX::WIC_FLAGS_NONE, + GUID_ContainerFormatPng, L"output.png")); + } } diff --git a/Win32CaptureSample/SimpleCapture.h b/Win32CaptureSample/SimpleCapture.h index ca0e282..29a2547 100644 --- a/Win32CaptureSample/SimpleCapture.h +++ b/Win32CaptureSample/SimpleCapture.h @@ -5,7 +5,8 @@ class SimpleCapture public: SimpleCapture( winrt::Windows::Graphics::DirectX::Direct3D11::IDirect3DDevice const& device, - winrt::Windows::Graphics::Capture::GraphicsCaptureItem const& item); + winrt::Windows::Graphics::Capture::GraphicsCaptureItem const& item, + winrt::Windows::Graphics::DirectX::DirectXPixelFormat pixelFormat); ~SimpleCapture() { Close(); } void StartCapture(); @@ -13,6 +14,7 @@ class SimpleCapture winrt::Windows::UI::Composition::Compositor const& compositor); void SaveNextFrame() { m_captureNextImage = true; } + winrt::Windows::Graphics::Capture::GraphicsCaptureItem CaptureItem() { return m_item; } void Close(); @@ -41,6 +43,7 @@ class SimpleCapture winrt::Windows::Graphics::DirectX::Direct3D11::IDirect3DDevice m_device{ nullptr }; winrt::com_ptr m_swapChain{ nullptr }; winrt::com_ptr m_d3dContext{ nullptr }; + winrt::Windows::Graphics::DirectX::DirectXPixelFormat m_pixelFormat; std::atomic m_closed = false; std::atomic m_captureNextImage = false; diff --git a/Win32CaptureSample/SimpleImageEncoder.cpp b/Win32CaptureSample/SimpleImageEncoder.cpp new file mode 100644 index 0000000..db49eaa --- /dev/null +++ b/Win32CaptureSample/SimpleImageEncoder.cpp @@ -0,0 +1,87 @@ +#include "pch.h" +#include "SimpleImageEncoder.h" + +namespace winrt +{ + using namespace Windows::Foundation; + using namespace Windows::Graphics; + using namespace Windows::Graphics::DirectX; + using namespace Windows::Graphics::DirectX::Direct3D11; + using namespace Windows::Storage; + using namespace Windows::Storage::Streams; +} + +struct WICSettings +{ + GUID ContainerFormat; + WICPixelFormatGUID PixelFormat; +}; + +WICSettings GetWICSettingsForFormat(SimpleImageEncoder::SupportedFormats format) +{ + switch (format) + { + case SimpleImageEncoder::SupportedFormats::Jpg: + return { GUID_ContainerFormatJpeg, GUID_WICPixelFormat32bppBGRA }; + case SimpleImageEncoder::SupportedFormats::Png: + return { GUID_ContainerFormatPng, GUID_WICPixelFormat32bppBGRA }; + case SimpleImageEncoder::SupportedFormats::Jxr: + return { GUID_ContainerFormatWmp, GUID_WICPixelFormat64bppRGBAHalf }; + } +} + +SimpleImageEncoder::SimpleImageEncoder(winrt::IDirect3DDevice const& device) +{ + m_device = device; + auto d3dDevice = GetDXGIInterfaceFromObject(m_device); + m_d2dFactory = CreateD2DFactory(); + m_d2dDevice = CreateD2DDevice(m_d2dFactory, d3dDevice); + winrt::check_hresult(m_d2dDevice->CreateDeviceContext(D2D1_DEVICE_CONTEXT_OPTIONS_NONE, m_d2dContext.put())); + m_wicFactory = CreateWICFactory(); +} + +void SimpleImageEncoder::EncodeImage(winrt::IDirect3DSurface const& surface, winrt::IRandomAccessStream const& stream, SupportedFormats const& format) +{ + auto abiStream = CreateStreamFromRandomAccessStream(stream); + + // Get the texture and setup the D2D bitmap + auto frameTexture = GetDXGIInterfaceFromObject(surface); + D3D11_TEXTURE2D_DESC textureDesc = {}; + frameTexture->GetDesc(&textureDesc); + auto dxgiFrameTexture = frameTexture.as(); + // TODO: Since this sample doesn't use D2D any other way, it may be better to map + // the pixels manually and hand them to WIC. However, using d2d is easier for now. + winrt::com_ptr d2dBitmap; + winrt::check_hresult(m_d2dContext->CreateBitmapFromDxgiSurface(dxgiFrameTexture.get(), nullptr, d2dBitmap.put())); + + // Get the WIC settings for the given format + auto wicSettings = GetWICSettingsForFormat(format); + + // Encode the image + // TODO: dpi? + auto dpi = 96.0f; + WICImageParameters params = {}; + params.PixelFormat.format = textureDesc.Format; + params.PixelFormat.alphaMode = D2D1_ALPHA_MODE_PREMULTIPLIED; + params.DpiX = dpi; + params.DpiY = dpi; + params.PixelWidth = textureDesc.Width; + params.PixelHeight = textureDesc.Height; + + winrt::com_ptr encoder; + winrt::check_hresult(m_wicFactory->CreateEncoder(wicSettings.ContainerFormat, nullptr, encoder.put())); + winrt::check_hresult(encoder->Initialize(abiStream.get(), WICBitmapEncoderNoCache)); + + winrt::com_ptr wicFrame; + winrt::com_ptr frameProperties; + winrt::check_hresult(encoder->CreateNewFrame(wicFrame.put(), frameProperties.put())); + winrt::check_hresult(wicFrame->Initialize(frameProperties.get())); + auto wicPixelFormat = wicSettings.PixelFormat; + winrt::check_hresult(wicFrame->SetPixelFormat(&wicPixelFormat)); + + winrt::com_ptr imageEncoder; + winrt::check_hresult(m_wicFactory->CreateImageEncoder(m_d2dDevice.get(), imageEncoder.put())); + winrt::check_hresult(imageEncoder->WriteFrame(d2dBitmap.get(), wicFrame.get(), ¶ms)); + winrt::check_hresult(wicFrame->Commit()); + winrt::check_hresult(encoder->Commit()); +} \ No newline at end of file diff --git a/Win32CaptureSample/SimpleImageEncoder.h b/Win32CaptureSample/SimpleImageEncoder.h new file mode 100644 index 0000000..40bcd53 --- /dev/null +++ b/Win32CaptureSample/SimpleImageEncoder.h @@ -0,0 +1,27 @@ +#pragma once + +class SimpleImageEncoder +{ +public: + SimpleImageEncoder(winrt::Windows::Graphics::DirectX::Direct3D11::IDirect3DDevice const& device); + ~SimpleImageEncoder() {} + + enum class SupportedFormats + { + Png, + Jpg, + Jxr + }; + + void EncodeImage( + winrt::Windows::Graphics::DirectX::Direct3D11::IDirect3DSurface const& surface, + winrt::Windows::Storage::Streams::IRandomAccessStream const& stream, + SupportedFormats const& format = SupportedFormats::Png); + +private: + winrt::Windows::Graphics::DirectX::Direct3D11::IDirect3DDevice m_device{ nullptr }; + winrt::com_ptr m_d2dFactory; + winrt::com_ptr m_d2dDevice; + winrt::com_ptr m_d2dContext; + winrt::com_ptr m_wicFactory; +}; \ No newline at end of file diff --git a/Win32CaptureSample/Win32CaptureSample.vcxproj b/Win32CaptureSample/Win32CaptureSample.vcxproj index 22a3bf9..ee8cb1e 100644 --- a/Win32CaptureSample/Win32CaptureSample.vcxproj +++ b/Win32CaptureSample/Win32CaptureSample.vcxproj @@ -202,6 +202,7 @@ + @@ -218,6 +219,7 @@ + diff --git a/Win32CaptureSample/Win32CaptureSample.vcxproj.filters b/Win32CaptureSample/Win32CaptureSample.vcxproj.filters index a49bc5c..38db3fb 100644 --- a/Win32CaptureSample/Win32CaptureSample.vcxproj.filters +++ b/Win32CaptureSample/Win32CaptureSample.vcxproj.filters @@ -9,6 +9,7 @@ + @@ -26,6 +27,7 @@ + diff --git a/Win32CaptureSample/WindowList.cpp b/Win32CaptureSample/WindowList.cpp index bea0b5a..f5a0ee0 100644 --- a/Win32CaptureSample/WindowList.cpp +++ b/Win32CaptureSample/WindowList.cpp @@ -1,6 +1,23 @@ #include "pch.h" #include "WindowList.h" +bool inline MatchTitleAndClassName(const WindowInfo const& window, std::wstring const& title, std::wstring const& className) +{ + return wcscmp(window.Title.c_str(), title.c_str()) == 0 && + wcscmp(window.ClassName.c_str(), className.c_str()) == 0; +} + +bool IsKnownBlockedWindow(const WindowInfo const& window) +{ + return + // Task View + MatchTitleAndClassName(window, L"Task View", L"Windows.UI.Core.CoreWindow") || + // XAML Islands + MatchTitleAndClassName(window, L"DesktopWindowXamlSource", L"Windows.UI.Core.CoreWindow") || + // XAML Popups + MatchTitleAndClassName(window, L"PopupHost", L"Xaml_WindowedPopupClass"); +} + bool IsCapturableWindow(const WindowInfo const& window) { if (window.Title.empty() || window.WindowHandle == GetShellWindow() || @@ -9,8 +26,14 @@ bool IsCapturableWindow(const WindowInfo const& window) return false; } - LONG style = GetWindowLongW(window.WindowHandle, GWL_STYLE); - if (!((style & WS_DISABLED) != WS_DISABLED)) + auto style = GetWindowLongW(window.WindowHandle, GWL_STYLE); + if (style & WS_DISABLED) + { + return false; + } + + auto exStyle = GetWindowLongW(window.WindowHandle, GWL_EXSTYLE); + if (exStyle & WS_EX_TOOLWINDOW) // No tooltips { return false; } @@ -27,7 +50,7 @@ bool IsCapturableWindow(const WindowInfo const& window) } // Unfortunate work-around. Not sure how to avoid this. - if (wcscmp(window.Title.c_str(), L"Task View") == 0) + if (IsKnownBlockedWindow(window)) { return false; } diff --git a/Win32CaptureSample/d3dHelpers.h b/Win32CaptureSample/d3dHelpers.h index 344f6b2..d15c31a 100644 --- a/Win32CaptureSample/d3dHelpers.h +++ b/Win32CaptureSample/d3dHelpers.h @@ -124,4 +124,56 @@ inline auto CreateDXGISwapChain(winrt::com_ptr const& device, desc.AlphaMode = DXGI_ALPHA_MODE_PREMULTIPLIED; return CreateDXGISwapChain(device, &desc); +} + +inline auto CreateDXGISwapChainForWindow(winrt::com_ptr const& device, const DXGI_SWAP_CHAIN_DESC1* desc, HWND window) +{ + auto dxgiDevice = device.as(); + winrt::com_ptr adapter; + winrt::check_hresult(dxgiDevice->GetParent(winrt::guid_of(), adapter.put_void())); + winrt::com_ptr factory; + winrt::check_hresult(adapter->GetParent(winrt::guid_of(), factory.put_void())); + + winrt::com_ptr swapchain; + winrt::check_hresult(factory->CreateSwapChainForHwnd(device.get(), window, desc, nullptr, nullptr, swapchain.put())); + return swapchain; +} + +inline auto CreateDXGISwapChainForWindow(winrt::com_ptr const& device, + uint32_t width, uint32_t height, DXGI_FORMAT format, uint32_t bufferCount, HWND window) +{ + DXGI_SWAP_CHAIN_DESC1 desc = {}; + desc.Width = width; + desc.Height = height; + desc.Format = format; + desc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT; + desc.SampleDesc.Count = 1; + desc.SampleDesc.Quality = 0; + desc.BufferCount = bufferCount; + desc.Scaling = DXGI_SCALING_NONE; + desc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL; + desc.AlphaMode = DXGI_ALPHA_MODE_IGNORE; + + return CreateDXGISwapChainForWindow(device, &desc, window); +} + +inline auto CopyD3DTexture(winrt::com_ptr const& device, winrt::com_ptr const& texture, bool asStagingTexture) +{ + winrt::com_ptr context; + device->GetImmediateContext(context.put()); + + D3D11_TEXTURE2D_DESC desc = {}; + texture->GetDesc(&desc); + // Clear flags that we don't need + desc.Usage = asStagingTexture ? D3D11_USAGE_STAGING : D3D11_USAGE_DEFAULT; + desc.BindFlags = asStagingTexture ? 0 : D3D11_BIND_SHADER_RESOURCE; + desc.CPUAccessFlags = asStagingTexture ? D3D11_CPU_ACCESS_READ : 0; + desc.MiscFlags = 0; + + // Create and fill the texture copy + winrt::com_ptr textureCopy; + winrt::check_hresult(device->CreateTexture2D(&desc, nullptr, textureCopy.put())); + context->CopyResource(textureCopy.get(), texture.get()); + + return textureCopy; } \ No newline at end of file diff --git a/Win32CaptureSample/main.cpp b/Win32CaptureSample/main.cpp index 83f8f9a..13391c8 100644 --- a/Win32CaptureSample/main.cpp +++ b/Win32CaptureSample/main.cpp @@ -1,5 +1,4 @@ #include "pch.h" -#include "SimpleCapture.h" #include "App.h" #include "SampleWindow.h"