From 7e9b970240ec10d416226666aa761131d459ee0f Mon Sep 17 00:00:00 2001 From: Matthew Parlane Date: Thu, 14 Nov 2013 21:02:49 +1300 Subject: [PATCH] Use libpng for saving images. TODO: Needs threading done similiar to OGL backend. Fixes issue 6779. --- Source/Core/VideoBackends/D3D/D3D.vcxproj | 6 + .../Core/VideoBackends/D3D/Src/D3DTexture.cpp | 177 +++++++----------- .../Core/VideoBackends/D3D/Src/D3DTexture.h | 2 +- Source/Core/VideoBackends/D3D/Src/Render.cpp | 2 +- .../VideoBackends/D3D/Src/TextureCache.cpp | 2 +- Source/VSProps/Base.props | 1 + 6 files changed, 78 insertions(+), 112 deletions(-) diff --git a/Source/Core/VideoBackends/D3D/D3D.vcxproj b/Source/Core/VideoBackends/D3D/D3D.vcxproj index a4177f6909..15d968b97f 100644 --- a/Source/Core/VideoBackends/D3D/D3D.vcxproj +++ b/Source/Core/VideoBackends/D3D/D3D.vcxproj @@ -93,9 +93,15 @@ + + {4c9f135b-a85e-430c-bad4-4c67ef5fc12c} + {1c8436c9-dbaf-42be-83bc-cf3ec9175abe} + + {ff213b23-2c26-4214-9f88-85271e557e87} + {3de9ee35-3e91-4f27-a014-2866ad8c3fe3} diff --git a/Source/Core/VideoBackends/D3D/Src/D3DTexture.cpp b/Source/Core/VideoBackends/D3D/Src/D3DTexture.cpp index fed4c69ef3..2b632b5466 100644 --- a/Source/Core/VideoBackends/D3D/Src/D3DTexture.cpp +++ b/Source/Core/VideoBackends/D3D/Src/D3DTexture.cpp @@ -5,9 +5,7 @@ #include "D3DBase.h" #include "D3DTexture.h" -#include -#include -#pragma comment(lib, "WindowsCodecs.lib") +#include "png.h" namespace DX11 { @@ -15,125 +13,86 @@ namespace DX11 namespace D3D { -HRESULT TextureToPng(D3D11_MAPPED_SUBRESOURCE &map, LPCWSTR wzFilename, int width, int height, bool saveAlpha) +bool TextureToPng(D3D11_MAPPED_SUBRESOURCE &map, const char* filename, 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)) + bool success = false; + if (map.pData != NULL) { - hr = piFactory->CreateStream(&piStream); - } + FILE *fp = NULL; + png_structp png_ptr = NULL; + png_infop info_ptr = NULL; - 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); + // Open file for writing (binary mode) + fp = fopen(filename, "wb"); + if (fp == NULL) { + PanicAlert("Could not open file %s for writing\n", filename); + goto finalise; } - } - if (SUCCEEDED(hr)) - { - hr = piBitmapFrame->SetSize(width, height); - } + // Initialize write structure + png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); + if (png_ptr == NULL) { + PanicAlert("Could not allocate write struct\n"); + goto finalise; + + } - WICPixelFormatGUID formatGUID = GUID_WICPixelFormat32bppBGRA; - if (SUCCEEDED(hr)) - { - hr = piBitmapFrame->SetPixelFormat(&formatGUID); - } + // Initialize info structure + info_ptr = png_create_info_struct(png_ptr); + if (info_ptr == NULL) { + PanicAlert("Could not allocate info struct\n"); + goto finalise; + } - 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; - } + // Setup Exception handling + if (setjmp(png_jmpbuf(png_ptr))) { + PanicAlert("Error during png creation\n"); + goto finalise; + } - if (SUCCEEDED(hr)) - { - if (map.pData != NULL) + png_init_io(png_ptr, fp); + + // Write header (8 bit colour depth) + png_set_IHDR(png_ptr, info_ptr, width, height, + 8, PNG_COLOR_TYPE_RGB_ALPHA, PNG_INTERLACE_NONE, + PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE); + + char title[] = "Dolphin Screenshot"; + png_text title_text; + title_text.compression = PNG_TEXT_COMPRESSION_NONE; + title_text.key = "Title"; + title_text.text = title; + png_set_text(png_ptr, info_ptr, &title_text, 1); + + png_write_info(png_ptr, info_ptr); + + // Write image data + for (auto y = 0; y < height; ++y) { - for (int y = 0; y < height; ++y) + u8* row_ptr = (u8*)map.pData + y * map.RowPitch; + u8* ptr = row_ptr; + for (UINT x = 0; x < map.RowPitch / 4; ++x) { - 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; - } + if (!saveAlpha) + ptr[3] = 0xff; + ptr += 4; } - hr = piBitmapFrame->WritePixels(height, map.RowPitch, height * map.RowPitch, (BYTE*)map.pData); - } - else - { - hr = E_OUTOFMEMORY; + png_write_row(png_ptr, row_ptr); } + + // End write + png_write_end(png_ptr, NULL); + + success = true; + + finalise: + + if (fp != NULL) fclose(fp); + if (info_ptr != NULL) png_free_data(png_ptr, info_ptr, PNG_FREE_ALL, -1); + if (png_ptr != NULL) png_destroy_write_struct(&png_ptr, (png_infopp)NULL); + } - - 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; + return false; } 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/D3DTexture.h b/Source/Core/VideoBackends/D3D/Src/D3DTexture.h index d047d3f249..71583d04ea 100644 --- a/Source/Core/VideoBackends/D3D/Src/D3DTexture.h +++ b/Source/Core/VideoBackends/D3D/Src/D3DTexture.h @@ -11,7 +11,7 @@ namespace DX11 namespace D3D { - HRESULT TextureToPng(D3D11_MAPPED_SUBRESOURCE& map, LPCWSTR wzFilename, int width, int height, bool saveAlpha = true); + bool TextureToPng(D3D11_MAPPED_SUBRESOURCE& map, const char* filename, 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 33528359c5..e2e26997c7 100644 --- a/Source/Core/VideoBackends/D3D/Src/Render.cpp +++ b/Source/Core/VideoBackends/D3D/Src/Render.cpp @@ -694,7 +694,7 @@ bool Renderer::SaveScreenshot(const std::string &filename, const TargetRectangle D3D::context->Map(s_screenshot_texture, 0, D3D11_MAP_READ_WRITE, 0, &map); // ready to be saved - HRESULT hr = D3D::TextureToPng(map, UTF8ToUTF16(filename.c_str()).c_str(), rc.GetWidth(), rc.GetHeight(), false); + HRESULT hr = D3D::TextureToPng(map, filename.c_str(), rc.GetWidth(), rc.GetHeight(), false); D3D::context->Unmap(s_screenshot_texture, 0); if (SUCCEEDED(hr)) diff --git a/Source/Core/VideoBackends/D3D/Src/TextureCache.cpp b/Source/Core/VideoBackends/D3D/Src/TextureCache.cpp index 9d1e28b9b0..0c6e759de5 100644 --- a/Source/Core/VideoBackends/D3D/Src/TextureCache.cpp +++ b/Source/Core/VideoBackends/D3D/Src/TextureCache.cpp @@ -62,7 +62,7 @@ bool TextureCache::TCacheEntry::Save(const char filename[], unsigned int level) 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); + hr = D3D::TextureToPng(map, filename, desc.Width, desc.Height); D3D::context->Unmap(pNewTexture, 0); } SAFE_RELEASE(pNewTexture); diff --git a/Source/VSProps/Base.props b/Source/VSProps/Base.props index 2ee5bd8e8e..1b40800891 100644 --- a/Source/VSProps/Base.props +++ b/Source/VSProps/Base.props @@ -44,6 +44,7 @@ $(ExternalsDir)Bochs_disasm;%(AdditionalIncludeDirectories) $(ExternalsDir)CLRun\include;%(AdditionalIncludeDirectories) $(ExternalsDir)GLew\include;%(AdditionalIncludeDirectories) + $(ExternalsDir)libpng;%(AdditionalIncludeDirectories) $(ExternalsDir)libusbx\libusb;%(AdditionalIncludeDirectories) $(ExternalsDir)LZO;%(AdditionalIncludeDirectories) $(ExternalsDir)miniupnpc\src;%(AdditionalIncludeDirectories)