diff --git a/Source/Core/VideoBackends/D3D/D3D.vcxproj b/Source/Core/VideoBackends/D3D/D3D.vcxproj index 15d968b97f..a4177f6909 100644 --- a/Source/Core/VideoBackends/D3D/D3D.vcxproj +++ b/Source/Core/VideoBackends/D3D/D3D.vcxproj @@ -93,15 +93,9 @@ - - {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 2b632b5466..abf34b3cca 100644 --- a/Source/Core/VideoBackends/D3D/Src/D3DTexture.cpp +++ b/Source/Core/VideoBackends/D3D/Src/D3DTexture.cpp @@ -5,96 +5,12 @@ #include "D3DBase.h" #include "D3DTexture.h" -#include "png.h" - namespace DX11 { namespace D3D { -bool TextureToPng(D3D11_MAPPED_SUBRESOURCE &map, const char* filename, int width, int height, bool saveAlpha) -{ - bool success = false; - if (map.pData != NULL) - { - FILE *fp = NULL; - png_structp png_ptr = NULL; - png_infop info_ptr = NULL; - - // 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; - } - - // 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; - - } - - // Initialize info structure - info_ptr = png_create_info_struct(png_ptr); - if (info_ptr == NULL) { - PanicAlert("Could not allocate info struct\n"); - goto finalise; - } - - // Setup Exception handling - if (setjmp(png_jmpbuf(png_ptr))) { - PanicAlert("Error during png creation\n"); - goto finalise; - } - - 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) - { - u8* row_ptr = (u8*)map.pData + y * map.RowPitch; - u8* ptr = row_ptr; - for (UINT x = 0; x < map.RowPitch / 4; ++x) - { - if (!saveAlpha) - ptr[3] = 0xff; - ptr += 4; - } - 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); - - } - return false; -} - 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 71583d04ea..5c2aa57f4a 100644 --- a/Source/Core/VideoBackends/D3D/Src/D3DTexture.h +++ b/Source/Core/VideoBackends/D3D/Src/D3DTexture.h @@ -11,7 +11,6 @@ namespace DX11 namespace D3D { - 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 e2e26997c7..ae6d21ef03 100644 --- a/Source/Core/VideoBackends/D3D/Src/Render.cpp +++ b/Source/Core/VideoBackends/D3D/Src/Render.cpp @@ -34,6 +34,7 @@ #include "FPSCounter.h" #include "ConfigManager.h" #include +#include "ImageWrite.h" namespace DX11 { @@ -693,11 +694,19 @@ bool Renderer::SaveScreenshot(const std::string &filename, const TargetRectangle D3D11_MAPPED_SUBRESOURCE map; D3D::context->Map(s_screenshot_texture, 0, D3D11_MAP_READ_WRITE, 0, &map); - // ready to be saved - HRESULT hr = D3D::TextureToPng(map, filename.c_str(), rc.GetWidth(), rc.GetHeight(), false); + bool saved_png = false; + if (map.pData) + { + u8* data = new u8[map.RowPitch * rc.GetHeight()]; + memcpy(data, map.pData, map.RowPitch * rc.GetHeight()); + + saved_png = TextureToPng(data, map.RowPitch, filename.c_str(), rc.GetWidth(), rc.GetHeight(), false); + } + D3D::context->Unmap(s_screenshot_texture, 0); - if (SUCCEEDED(hr)) + + if (saved_png) { OSD::AddMessage(StringFromFormat("Saved %i x %i %s", rc.GetWidth(), rc.GetHeight(), filename.c_str())); @@ -707,7 +716,7 @@ bool Renderer::SaveScreenshot(const std::string &filename, const TargetRectangle OSD::AddMessage(StringFromFormat("Error saving %s", filename.c_str())); } - return SUCCEEDED(hr); + return saved_png; } void formatBufferDump(const u8* in, u8* out, int w, int h, int p) diff --git a/Source/Core/VideoBackends/D3D/Src/TextureCache.cpp b/Source/Core/VideoBackends/D3D/Src/TextureCache.cpp index 0c6e759de5..faf83c23ca 100644 --- a/Source/Core/VideoBackends/D3D/Src/TextureCache.cpp +++ b/Source/Core/VideoBackends/D3D/Src/TextureCache.cpp @@ -14,6 +14,7 @@ #include "PSTextureEncoder.h" #include "HW/Memmap.h" #include "VideoConfig.h" +#include "ImageWrite.h" namespace DX11 { @@ -54,6 +55,8 @@ bool TextureCache::TCacheEntry::Save(const char filename[], unsigned int level) HRESULT hr = D3D::device->CreateTexture2D(&desc, NULL, &pNewTexture); + bool saved_png = false; + if (SUCCEEDED(hr) && pNewTexture) { D3D::context->CopyResource(pNewTexture, pSurface); @@ -62,13 +65,19 @@ 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, filename, desc.Width, desc.Height); + if (map.pData) + { + u8* data = new u8[map.RowPitch * desc.Height]; + memcpy(data, map.pData, map.RowPitch * desc.Height); + + saved_png = TextureToPng(data, map.RowPitch, filename, desc.Width, desc.Height); + } D3D::context->Unmap(pNewTexture, 0); } SAFE_RELEASE(pNewTexture); } - return SUCCEEDED(hr); + return saved_png; } void TextureCache::TCacheEntry::Load(unsigned int width, unsigned int height, diff --git a/Source/Core/VideoBackends/OGL/Src/Render.cpp b/Source/Core/VideoBackends/OGL/Src/Render.cpp index 020fac0d43..b32fc74f34 100644 --- a/Source/Core/VideoBackends/OGL/Src/Render.cpp +++ b/Source/Core/VideoBackends/OGL/Src/Render.cpp @@ -63,10 +63,6 @@ #include "AVIDump.h" #endif -#if defined(HAVE_WX) && HAVE_WX -#include -#endif - // glew1.8 doesn't define KHR_debug #ifndef GL_DEBUG_OUTPUT #define GL_DEBUG_OUTPUT 0x92E0 @@ -78,18 +74,6 @@ void VideoConfig::UpdateProjectionHack() ::UpdateProjectionHack(g_Config.iPhackvalue, g_Config.sPhackvalue); } - -#if defined(HAVE_WX) && HAVE_WX -// Screenshot thread struct -typedef struct -{ - int W, H; - std::string filename; - wxImage *img; -} ScrStrct; -#endif - - int OSDInternalW, OSDInternalH; namespace OGL @@ -1804,69 +1788,21 @@ void Renderer::SetInterlacingMode() // TODO } -void Renderer::FlipImageData(u8 *data, int w, int h) +void Renderer::FlipImageData(u8 *data, int w, int h, int pixel_width) { // Flip image upside down. Damn OpenGL. - for (int y = 0; y < h / 2; y++) + for (int y = 0; y < h / 2; ++y) { - for(int x = 0; x < w; x++) + for(int x = 0; x < w; ++x) { - std::swap(data[(y * w + x) * 3], data[((h - 1 - y) * w + x) * 3]); - std::swap(data[(y * w + x) * 3 + 1], data[((h - 1 - y) * w + x) * 3 + 1]); - std::swap(data[(y * w + x) * 3 + 2], data[((h - 1 - y) * w + x) * 3 + 2]); + for (auto delta = 0; delta < pixel_width; ++delta) + std::swap(data[(y * w + x) * pixel_width + delta], data[((h - 1 - y) * w + x) * pixel_width + delta]); } } } } -// TODO: remove -extern bool g_aspect_wide; - -#if defined(HAVE_WX) && HAVE_WX -void TakeScreenshot(ScrStrct* threadStruct) -{ - // These will contain the final image size - float FloatW = (float)threadStruct->W; - float FloatH = (float)threadStruct->H; - - // Handle aspect ratio for the final ScrStrct to look exactly like what's on screen. - if (g_ActiveConfig.iAspectRatio != ASPECT_STRETCH) - { - bool use16_9 = g_aspect_wide; - - // Check for force-settings and override. - if (g_ActiveConfig.iAspectRatio == ASPECT_FORCE_16_9) - use16_9 = true; - else if (g_ActiveConfig.iAspectRatio == ASPECT_FORCE_4_3) - use16_9 = false; - - float Ratio = (FloatW / FloatH) / (!use16_9 ? (4.0f / 3.0f) : (16.0f / 9.0f)); - - // If ratio > 1 the picture is too wide and we have to limit the width. - if (Ratio > 1) - FloatW /= Ratio; - // ratio == 1 or the image is too high, we have to limit the height. - else - FloatH *= Ratio; - - // This is a bit expensive on high resolutions - threadStruct->img->Rescale((int)FloatW, (int)FloatH, wxIMAGE_QUALITY_HIGH); - } - - // Save the screenshot and finally kill the wxImage object - // This is really expensive when saving to PNG, but not at all when using BMP - threadStruct->img->SaveFile(StrToWxStr(threadStruct->filename), - wxBITMAP_TYPE_PNG); - threadStruct->img->Destroy(); - - // Show success messages - OSD::AddMessage(StringFromFormat("Saved %i x %i %s", (int)FloatW, (int)FloatH, - threadStruct->filename.c_str()), 2000); - delete threadStruct; -} -#endif - namespace OGL { @@ -1874,10 +1810,10 @@ bool Renderer::SaveScreenshot(const std::string &filename, const TargetRectangle { u32 W = back_rc.GetWidth(); u32 H = back_rc.GetHeight(); - u8 *data = (u8 *)malloc((sizeof(u8) * 3 * W * H)); + u8 *data = (u8 *)malloc((sizeof(u8) * 4 * W * H)); glPixelStorei(GL_PACK_ALIGNMENT, 1); - glReadPixels(back_rc.left, back_rc.bottom, W, H, GL_RGB, GL_UNSIGNED_BYTE, data); + glReadPixels(back_rc.left, back_rc.bottom, W, H, GL_RGBA, GL_UNSIGNED_BYTE, data); // Show failure message if (GL_REPORT_ERROR() != GL_NO_ERROR) @@ -1888,34 +1824,9 @@ bool Renderer::SaveScreenshot(const std::string &filename, const TargetRectangle } // Turn image upside down - FlipImageData(data, W, H); + FlipImageData(data, W, H, 4); -#if defined(HAVE_WX) && HAVE_WX - // Create wxImage - wxImage *a = new wxImage(W, H, data); - - if (scrshotThread.joinable()) - scrshotThread.join(); - - ScrStrct *threadStruct = new ScrStrct; - threadStruct->filename = filename; - threadStruct->img = a; - threadStruct->H = H; threadStruct->W = W; - - scrshotThread = std::thread(TakeScreenshot, threadStruct); -#ifdef _WIN32 - SetThreadPriority(scrshotThread.native_handle(), THREAD_PRIORITY_BELOW_NORMAL); -#endif - bool result = true; - - OSD::AddMessage("Saving Screenshot... ", 2000); - -#else - bool result = SaveTGA(filename.c_str(), W, H, data); - free(data); -#endif - - return result; + return TextureToPng(data, W*4, filename.c_str(), W, H, false); } } diff --git a/Source/Core/VideoBackends/OGL/Src/Render.h b/Source/Core/VideoBackends/OGL/Src/Render.h index 9b4838ee25..0cda2f4c0e 100644 --- a/Source/Core/VideoBackends/OGL/Src/Render.h +++ b/Source/Core/VideoBackends/OGL/Src/Render.h @@ -63,7 +63,7 @@ public: void RenderText(const char* pstr, int left, int top, u32 color) override; void DrawDebugInfo(); - void FlipImageData(u8 *data, int w, int h); + void FlipImageData(u8 *data, int w, int h, int pixel_width = 3); u32 AccessEFB(EFBAccessType type, u32 x, u32 y, u32 poke_data) override; diff --git a/Source/Core/VideoCommon/Src/ImageWrite.cpp b/Source/Core/VideoCommon/Src/ImageWrite.cpp index f53bd1c18f..f42e89cfd0 100644 --- a/Source/Core/VideoCommon/Src/ImageWrite.cpp +++ b/Source/Core/VideoCommon/Src/ImageWrite.cpp @@ -5,6 +5,7 @@ #include #include +#include "png.h" #include "ImageWrite.h" #include "FileUtil.h" @@ -62,3 +63,95 @@ bool SaveData(const char* filename, const char* data) return true; } + + +/* +TextureToPng + +Inputs: +data : This is an array of RGBA with 8 bits per channel. 4 bytes for each pixel. +data is a newly allocated memory and must have delete[] run on it before returning. + +row_stride: Determines the amount of bytes per row of pixels. +*/ +bool TextureToPng(u8* data, int row_stride, const char* filename, int width, int height, bool saveAlpha) +{ + bool success = false; + + if (!data) + return false; + + // Open file for writing (binary mode) + FILE *fp = fopen(filename, "wb"); + if (fp == NULL) { + PanicAlert("Screenshot failed: Could not open file %s\n", filename); + goto finalise; + } + + // Initialize write structure + png_structp png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); + if (png_ptr == NULL) { + PanicAlert("Screenshot failed: Could not allocate write struct\n"); + goto finalise; + + } + + // Initialize info structure + png_infop info_ptr = png_create_info_struct(png_ptr); + if (info_ptr == NULL) { + PanicAlert("Screenshot failed: Could not allocate info struct\n"); + goto finalise; + } + + // Setup Exception handling + if (setjmp(png_jmpbuf(png_ptr))) { + PanicAlert("Screenshot failed: Error during png creation\n"); + goto finalise; + } + + 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) + { + u8* row_ptr = (u8*)data + y * row_stride; + u8* ptr = row_ptr; + for (auto x = 0; x < row_stride / 4; ++x) + { + if (!saveAlpha) + ptr[3] = 0xff; + ptr += 4; + } + 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); + + // Our duty to delete the inputted data. + delete[] data; + + return success; +} diff --git a/Source/Core/VideoCommon/Src/ImageWrite.h b/Source/Core/VideoCommon/Src/ImageWrite.h index 7d1d6fa1e8..af7f11ad27 100644 --- a/Source/Core/VideoCommon/Src/ImageWrite.h +++ b/Source/Core/VideoCommon/Src/ImageWrite.h @@ -9,6 +9,7 @@ bool SaveTGA(const char* filename, int width, int height, void* pdata); bool SaveData(const char* filename, const char* pdata); +bool TextureToPng(u8* data, int row_stride, const char* filename, int width, int height, bool saveAlpha = true); #endif // _IMAGEWRITE_H diff --git a/Source/Core/VideoCommon/VideoCommon.vcxproj b/Source/Core/VideoCommon/VideoCommon.vcxproj index dd8bbc6f6d..fa7ef921ae 100644 --- a/Source/Core/VideoCommon/VideoCommon.vcxproj +++ b/Source/Core/VideoCommon/VideoCommon.vcxproj @@ -152,9 +152,15 @@ {aa862e5e-a993-497a-b6a0-0e8e94b10050} + + {4c9f135b-a85e-430c-bad4-4c67ef5fc12c} + {b441cc62-877e-4b3f-93e0-0de80544f705} + + {ff213b23-2c26-4214-9f88-85271e557e87} + {2e6c348c-c75c-4d94-8d1e-9c1fcbf3efe4}