From 7b53574b68be91e29b57939c003809855fff6c5c Mon Sep 17 00:00:00 2001 From: Matthew Parlane Date: Thu, 14 Nov 2013 00:48:02 +1300 Subject: [PATCH] Support texture and screenshot dumping using WIC, no XP support. --- .../Core/VideoBackends/D3D/Src/D3DTexture.cpp | 126 ++++++++++++++++++ .../Core/VideoBackends/D3D/Src/D3DTexture.h | 1 + Source/Core/VideoBackends/D3D/Src/Render.cpp | 15 +-- .../VideoBackends/D3D/Src/TextureCache.cpp | 28 +++- 4 files changed, 157 insertions(+), 13 deletions(-) diff --git a/Source/Core/VideoBackends/D3D/Src/D3DTexture.cpp b/Source/Core/VideoBackends/D3D/Src/D3DTexture.cpp index abf34b3cca..94d778d17b 100644 --- a/Source/Core/VideoBackends/D3D/Src/D3DTexture.cpp +++ b/Source/Core/VideoBackends/D3D/Src/D3DTexture.cpp @@ -5,12 +5,138 @@ #include "D3DBase.h" #include "D3DTexture.h" +#include +#include +#include +#pragma comment(lib, "WindowsCodecs.lib") + namespace DX11 { namespace D3D { +HRESULT TextureToPng(D3D11_MAPPED_SUBRESOURCE &map, LPCWSTR wzFilename, int width, int height, bool saveAlpha) +{ + IWICImagingFactory *piFactory = NULL; + IWICBitmapEncoder *piEncoder = NULL; + IWICBitmapFrameEncode *piBitmapFrame = NULL; + IPropertyBag2 *pPropertybag = NULL; + + IWICStream *piStream = NULL; + + HRESULT hr = CoCreateInstance( + CLSID_WICImagingFactory, + NULL, + CLSCTX_INPROC_SERVER, + IID_IWICImagingFactory, + (LPVOID*)&piFactory); + + if (SUCCEEDED(hr)) + { + hr = piFactory->CreateStream(&piStream); + } + + if (SUCCEEDED(hr)) + { + hr = piStream->InitializeFromFilename(wzFilename, GENERIC_WRITE); + } + + if (SUCCEEDED(hr)) + { + hr = piFactory->CreateEncoder(GUID_ContainerFormatPng, NULL, &piEncoder); + } + + if (SUCCEEDED(hr)) + { + hr = piEncoder->Initialize(piStream, WICBitmapEncoderNoCache); + } + + if (SUCCEEDED(hr)) + { + hr = piEncoder->CreateNewFrame(&piBitmapFrame, &pPropertybag); + } + + if (SUCCEEDED(hr)) + { + if (SUCCEEDED(hr)) + { + hr = piBitmapFrame->Initialize(pPropertybag); + } + } + + if (SUCCEEDED(hr)) + { + hr = piBitmapFrame->SetSize(width, height); + } + + WICPixelFormatGUID formatGUID = GUID_WICPixelFormat32bppBGRA; + if (SUCCEEDED(hr)) + { + hr = piBitmapFrame->SetPixelFormat(&formatGUID); + } + + if (SUCCEEDED(hr)) + { + // We're expecting to write out 32bppBGRA. Fail if the encoder cannot do it. + hr = IsEqualGUID(formatGUID, GUID_WICPixelFormat32bppBGRA) ? S_OK : E_FAIL; + } + + if (SUCCEEDED(hr)) + { + if (map.pData != NULL) + { + for (int y = 0; y < height; ++y) + { + u8* ptr = (u8*)map.pData + y * map.RowPitch; + for (unsigned int x = 0; x < map.RowPitch/4; ++x) + { + u8 r = ptr[0]; + u8 g = ptr[1]; + u8 b = ptr[2]; + ptr[0] = b; + ptr[1] = g; + ptr[2] = r; + if (!saveAlpha) + ptr[3] = 0xff; + + + ptr += 4; + } + } + hr = piBitmapFrame->WritePixels(height, map.RowPitch, height * map.RowPitch, (BYTE*)map.pData); + } + else + { + hr = E_OUTOFMEMORY; + } + } + + if (SUCCEEDED(hr)) + { + hr = piBitmapFrame->Commit(); + } + + if (SUCCEEDED(hr)) + { + hr = piEncoder->Commit(); + } + + if (piFactory) + piFactory->Release(); + + if (piBitmapFrame) + piBitmapFrame->Release(); + + if (piEncoder) + piEncoder->Release(); + + if (piStream) + piStream->Release(); + + return hr; +} + void ReplaceRGBATexture2D(ID3D11Texture2D* pTexture, const u8* buffer, unsigned int width, unsigned int height, unsigned int pitch, unsigned int level, D3D11_USAGE usage) { if (usage == D3D11_USAGE_DYNAMIC || usage == D3D11_USAGE_STAGING) diff --git a/Source/Core/VideoBackends/D3D/Src/D3DTexture.h b/Source/Core/VideoBackends/D3D/Src/D3DTexture.h index 5c2aa57f4a..d047d3f249 100644 --- a/Source/Core/VideoBackends/D3D/Src/D3DTexture.h +++ b/Source/Core/VideoBackends/D3D/Src/D3DTexture.h @@ -11,6 +11,7 @@ namespace DX11 namespace D3D { + HRESULT TextureToPng(D3D11_MAPPED_SUBRESOURCE& map, LPCWSTR wzFilename, int width, int height, bool saveAlpha = true); void ReplaceRGBATexture2D(ID3D11Texture2D* pTexture, const u8* buffer, unsigned int width, unsigned int height, unsigned int pitch, unsigned int level, D3D11_USAGE usage); } diff --git a/Source/Core/VideoBackends/D3D/Src/Render.cpp b/Source/Core/VideoBackends/D3D/Src/Render.cpp index c7ad9e2bda..33528359c5 100644 --- a/Source/Core/VideoBackends/D3D/Src/Render.cpp +++ b/Source/Core/VideoBackends/D3D/Src/Render.cpp @@ -690,22 +690,13 @@ bool Renderer::SaveScreenshot(const std::string &filename, const TargetRectangle D3D11_BOX box = CD3D11_BOX(rc.left, rc.top, 0, rc.right, rc.bottom, 1); D3D::context->CopySubresourceRegion(s_screenshot_texture, 0, 0, 0, 0, (ID3D11Resource*)D3D::GetBackBuffer()->GetTex(), 0, &box); - // D3DX11SaveTextureToFileA doesn't allow us to ignore the alpha channel, so we need to strip it out ourselves D3D11_MAPPED_SUBRESOURCE map; D3D::context->Map(s_screenshot_texture, 0, D3D11_MAP_READ_WRITE, 0, &map); - for (auto y = 0; y < rc.GetHeight(); ++y) - { - u8* ptr = (u8*)map.pData + y * map.RowPitch + 3; - for (auto x = 0; x < rc.GetWidth(); ++x) - { - *ptr = 0xFF; - ptr += 4; - } - } - D3D::context->Unmap(s_screenshot_texture, 0); // ready to be saved - HRESULT hr = PD3DX11SaveTextureToFileA(D3D::context, s_screenshot_texture, D3DX11_IFF_PNG, filename.c_str()); + HRESULT hr = D3D::TextureToPng(map, UTF8ToUTF16(filename.c_str()).c_str(), rc.GetWidth(), rc.GetHeight(), false); + D3D::context->Unmap(s_screenshot_texture, 0); + if (SUCCEEDED(hr)) { OSD::AddMessage(StringFromFormat("Saved %i x %i %s", rc.GetWidth(), diff --git a/Source/Core/VideoBackends/D3D/Src/TextureCache.cpp b/Source/Core/VideoBackends/D3D/Src/TextureCache.cpp index d3c536c4a6..9d1e28b9b0 100644 --- a/Source/Core/VideoBackends/D3D/Src/TextureCache.cpp +++ b/Source/Core/VideoBackends/D3D/Src/TextureCache.cpp @@ -42,7 +42,33 @@ bool TextureCache::TCacheEntry::Save(const char filename[], unsigned int level) warn_once = false; return false; } - return SUCCEEDED(PD3DX11SaveTextureToFileA(D3D::context, texture->GetTex(), D3DX11_IFF_PNG, filename)); + + ID3D11Texture2D* pNewTexture = NULL; + ID3D11Texture2D* pSurface = texture->GetTex(); + D3D11_TEXTURE2D_DESC desc; + pSurface->GetDesc(&desc); + + desc.BindFlags = 0; + desc.CPUAccessFlags = D3D11_CPU_ACCESS_READ | D3D11_CPU_ACCESS_WRITE; + desc.Usage = D3D11_USAGE_STAGING; + + HRESULT hr = D3D::device->CreateTexture2D(&desc, NULL, &pNewTexture); + + if (SUCCEEDED(hr) && pNewTexture) + { + D3D::context->CopyResource(pNewTexture, pSurface); + + D3D11_MAPPED_SUBRESOURCE map; + HRESULT hr = D3D::context->Map(pNewTexture, 0, D3D11_MAP_READ_WRITE, 0, &map); + if (SUCCEEDED(hr)) + { + hr = D3D::TextureToPng(map, UTF8ToUTF16(filename).c_str(), desc.Width, desc.Height); + D3D::context->Unmap(pNewTexture, 0); + } + SAFE_RELEASE(pNewTexture); + } + + return SUCCEEDED(hr); } void TextureCache::TCacheEntry::Load(unsigned int width, unsigned int height,