From 87dcfbc645a5326e3a7d64ad0ffa9b5bbf57f7d3 Mon Sep 17 00:00:00 2001 From: Jonathan Li Date: Sun, 21 Feb 2016 09:59:49 +0000 Subject: [PATCH 1/9] gsdx-png: Use libpng directly instead of via png++ v2: Increase compression to maximum v3: Add zlib to GSdx build dependencies v4: Reduce memory usage --- cmake/SelectPcsx2Plugins.cmake | 6 +- plugins/GSdx/GSCapture.cpp | 4 +- plugins/GSdx/GSPng.cpp | 217 +++++++++++++++------------------ plugins/GSdx/GSPng.h | 13 +- plugins/GSdx/GSTextureOGL.cpp | 19 ++- plugins/GSdx/GSTextureSW.cpp | 3 +- 6 files changed, 119 insertions(+), 143 deletions(-) diff --git a/cmake/SelectPcsx2Plugins.cmake b/cmake/SelectPcsx2Plugins.cmake index 1f400bd921..6ceb22aef6 100644 --- a/cmake/SelectPcsx2Plugins.cmake +++ b/cmake/SelectPcsx2Plugins.cmake @@ -148,9 +148,11 @@ endif() # GSdx #--------------------------------------- # requires: -OpenGL -# -X11 +# -PNG +# -X11 +# -zlib #--------------------------------------- -if(OPENGL_FOUND AND X11_FOUND AND GTKn_FOUND AND PNG_FOUND AND (EGL_FOUND OR NOT EGL_API)) +if(OPENGL_FOUND AND X11_FOUND AND GTKn_FOUND AND ZLIB_FOUND AND PNG_FOUND AND (EGL_FOUND OR NOT EGL_API)) set(GSdx TRUE) elseif(NOT EXISTS "${CMAKE_SOURCE_DIR}/plugins/GSdx") set(GSdx FALSE) diff --git a/plugins/GSdx/GSCapture.cpp b/plugins/GSdx/GSCapture.cpp index 809cfd7f42..75cd0c1066 100644 --- a/plugins/GSdx/GSCapture.cpp +++ b/plugins/GSdx/GSCapture.cpp @@ -524,8 +524,8 @@ bool GSCapture::DeliverFrame(const void* bits, int pitch, bool rgba) #elif __linux__ std::string out_file = m_out_dir + format("/frame.%010d.png", m_frame); - //GSPng::Save(GSPng::RGB_PNG, out_file, (char*)bits, m_size.x, m_size.y, pitch); - m_workers[m_frame%m_threads]->Push(shared_ptr(new GSPng::Transaction(GSPng::RGB_PNG, out_file, (char*)bits, m_size.x, m_size.y, pitch))); + //GSPng::Save(GSPng::RGB_PNG, out_file, (uint8*)bits, m_size.x, m_size.y, pitch); + m_workers[m_frame%m_threads]->Push(shared_ptr(new GSPng::Transaction(GSPng::RGB_PNG, out_file, static_cast(bits), m_size.x, m_size.y, pitch))); m_frame++; diff --git a/plugins/GSdx/GSPng.cpp b/plugins/GSdx/GSPng.cpp index f4a87b9027..64654236d9 100644 --- a/plugins/GSdx/GSPng.cpp +++ b/plugins/GSdx/GSPng.cpp @@ -20,143 +20,120 @@ #include "stdafx.h" #include "GSPng.h" +#include +#include + +struct { + int type; + int bytes_per_pixel_in; + int bytes_per_pixel_out; + int channel_bit_depth; + const char *extension[2]; +} static const pixel[GSPng::Format::COUNT] = { + {PNG_COLOR_TYPE_RGBA, 4, 4, 8 , {"_full.png", nullptr}}, // RGBA_PNG + {PNG_COLOR_TYPE_RGB , 4, 3, 8 , {".png", nullptr}}, // RGB_PNG + {PNG_COLOR_TYPE_RGB , 4, 3, 8 , {".png", "_alpha.png"}}, // RGB_A_PNG + {PNG_COLOR_TYPE_GRAY, 4, 1, 8 , {"_alpha.png", nullptr}}, // ALPHA_PNG + {PNG_COLOR_TYPE_GRAY, 4, 2, 16, {"_lsb.png", "_msb.png"}}, // DEPTH_PNG + {PNG_COLOR_TYPE_GRAY, 1, 1, 8 , {"_R8I.png", nullptr}}, // R8I_PNG + {PNG_COLOR_TYPE_GRAY, 2, 2, 16, {"_R16I.png", nullptr}}, // R16I_PNG + {PNG_COLOR_TYPE_GRAY, 4, 2, 16, {"_R32I_lsb.png", "_R32I_msb.png"}}, // R32I_PNG +}; namespace GSPng { - // FIXME gray_pixel_16 doesn't work. Integer image and depth image are all black - // Maybe I can't open them correctly - // A better solution must be found! - void Save(GSPng::Format fmt, const string& file, char* image, int w, int h, int pitch) + bool SaveFile(const string& file, Format fmt, uint8* image, uint8* row, + int width, int height, int pitch, + bool rb_swapped = false, bool first_image = false) + { + int channel_bit_depth = pixel[fmt].channel_bit_depth; + int bytes_per_pixel_in = pixel[fmt].bytes_per_pixel_in; + + int type = first_image ? pixel[fmt].type : PNG_COLOR_TYPE_GRAY; + int offset = first_image ? 0 : pixel[fmt].bytes_per_pixel_out; + int bytes_per_pixel_out = first_image ? pixel[fmt].bytes_per_pixel_out : bytes_per_pixel_in - offset; + + FILE *fp = fopen(file.c_str(), "wb"); + if (fp == nullptr) + return false; + + png_structp png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, nullptr, nullptr, nullptr); + png_infop info_ptr = nullptr; + + bool success = false; + try { + if (png_ptr == nullptr) + throw GSDXRecoverableError(); + + info_ptr = png_create_info_struct(png_ptr); + if (info_ptr == nullptr) + throw GSDXRecoverableError(); + + if (setjmp(png_jmpbuf(png_ptr))) + throw GSDXRecoverableError(); + + png_init_io(png_ptr, fp); + png_set_compression_level(png_ptr, Z_BEST_COMPRESSION); + png_set_IHDR(png_ptr, info_ptr, width, height, channel_bit_depth, type, + PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT); + png_write_info(png_ptr, info_ptr); + + if (channel_bit_depth > 8) + png_set_swap(png_ptr); + if (rb_swapped && type != PNG_COLOR_TYPE_GRAY) + png_set_bgr(png_ptr); + + for (int y = 0; y < height; ++y, image += pitch) { + for (int x = 0; x < width; ++x) + for (int i = 0; i < bytes_per_pixel_out; ++i) + row[bytes_per_pixel_out * x + i] = image[bytes_per_pixel_in * x + i + offset]; + png_write_row(png_ptr, row); + } + png_write_end(png_ptr, nullptr); + + success = true; + } + catch (GSDXRecoverableError&) { + fprintf(stderr, "Failed to write image %s\n", file.c_str()); + } + + if (png_ptr) + png_destroy_write_struct(&png_ptr, info_ptr ? &info_ptr : nullptr); + fclose(fp); + + return success; + } + + bool Save(GSPng::Format fmt, const string& file, uint8* image, int w, int h, int pitch, bool rb_swapped) { #ifdef ENABLE_OGL_PNG std::string root = file; - root.replace(file.length()-4, 4, ""); + root.replace(file.length() - 4, 4, ""); - uint8* data = (uint8*)image; + ASSERT(fmt >= Format::START && fmt < Format::COUNT); - switch (fmt) { - case R8I_PNG: - { - png::image img(w, h); - for(int y = 0; y < h; y++, data += pitch) { - for (int x = 0; x < w; x++) { - img[y][x] = png::gray_pixel(data[x]); - } - } - img.write(root + "_R8.png"); - } - break; + std::unique_ptr row(new uint8[pixel[fmt].bytes_per_pixel_out * w]); - case R16I_PNG: - { - png::image img(w, h); - for(int y = 0; y < h; y++, data += pitch) { - for (int x = 0; x < w; x++) { - img[y][x] = png::gray_pixel_16(data[2*x]); - } - } - img.write(root + "_R16.png"); - } - break; + std::string filename = root + pixel[fmt].extension[0]; + if (!SaveFile(filename, fmt, image, row.get(), w, h, pitch, rb_swapped, true)) + return false; - case R32I_PNG: - { - png::image img_msb(w, h); - png::image img_lsb(w, h); - for(int y = 0; y < h; y++, data += pitch) { - for (int x = 0; x < w; x++) { - img_msb[y][x] = png::gray_pixel_16(data[2*x]); - img_lsb[y][x] = png::gray_pixel_16(data[2*x+2]); - } - } - img_msb.write(root + "_R32I_msb.png"); - img_lsb.write(root + "_R32I_lsb.png"); - } - break; + // Second image + if (pixel[fmt].extension[1] == nullptr) + return true; - case DEPTH_PNG: - { - png::image img_msb(w, h); - png::image img_lsb(w, h); - for(int y = 0; y < h; y++, data += pitch) { - for (int x = 0; x < w; x++) { - // TODO packed or not - uint32 depth = data[4*x]; //floorf((float)data[2*x] * exp2f(32)); - - png::gray_pixel_16 msb(depth >> 16); - png::gray_pixel_16 lsb((depth >> 16) ? 0xFFFF : depth & 0xFFFF); - - img_msb[y][x] = msb; - img_lsb[y][x] = lsb; - } - } - img_msb.write(root + "_msb.png"); - img_lsb.write(root + "_lsb.png"); - } - break; - - case ALPHA_PNG: - { - png::image img_alpha(w, h); - for(int y = 0; y < h; y++, data += pitch) { - for (int x = 0; x < w; x++) { - img_alpha[y][x] = png::gray_pixel(data[4*x+3]); - } - } - img_alpha.write(root + "_alpha.png"); - } - break; - - case RGB_PNG: - { - png::image img_opaque(w, h); - for(int y = 0; y < h; y++, data += pitch) { - for (int x = 0; x < w; x++) { - img_opaque[y][x] = png::rgb_pixel(data[4*x+0], data[4*x+1], data[4*x+2]); - } - } - img_opaque.write(root + ".png"); - } - break; - - case RGBA_PNG: - { - png::image img(w, h); - for(int y = 0; y < h; y++, data += pitch) { - for (int x = 0; x < w; x++) { - img[y][x] = png::rgba_pixel(data[4*x+0], data[4*x+1], data[4*x+2], data[4*x+3]); - } - } - img.write(root + "_full.png"); - } - break; - - case RGB_A_PNG: - { - png::image img_opaque(w, h); - png::image img_alpha(w, h); - for(int y = 0; y < h; y++, data += pitch) { - for (int x = 0; x < w; x++) { - img_opaque[y][x] = png::rgb_pixel(data[4*x+0], data[4*x+1], data[4*x+2]); - img_alpha[y][x] = png::gray_pixel(data[4*x+3]); - } - } - img_opaque.write(root + ".png"); - img_alpha.write(root + "_alpha.png"); - } - break; - - default: - ASSERT(0); - } + filename = root + pixel[fmt].extension[1]; + return SaveFile(filename, fmt, image, row.get(), w, h, pitch); +#else + return false; #endif } - Transaction::Transaction(GSPng::Format fmt, const string& file, char* image, int w, int h, int pitch) + Transaction::Transaction(GSPng::Format fmt, const string& file, const uint8* image, int w, int h, int pitch) : m_fmt(fmt), m_file(file), m_w(w), m_h(h), m_pitch(pitch) { // Note: yes it would be better to use shared pointer - m_image = (char*)_aligned_malloc(pitch*h, 32); + m_image = (uint8*)_aligned_malloc(pitch*h, 32); if (m_image) memcpy(m_image, image, pitch*h); } diff --git a/plugins/GSdx/GSPng.h b/plugins/GSdx/GSPng.h index b328e89502..db881e7f47 100644 --- a/plugins/GSdx/GSPng.h +++ b/plugins/GSdx/GSPng.h @@ -20,14 +20,12 @@ #pragma once -#ifdef ENABLE_OGL_PNG -#include "png++/png.hpp" -#endif #include "GSThread_CXX11.h" namespace GSPng { enum Format { - RGBA_PNG, + START = 0, + RGBA_PNG = 0, RGB_PNG, RGB_A_PNG, ALPHA_PNG, @@ -35,6 +33,7 @@ namespace GSPng { R8I_PNG, R16I_PNG, R32I_PNG, + COUNT }; class Transaction @@ -42,16 +41,16 @@ namespace GSPng { public: Format m_fmt; const std::string m_file; - char* m_image; + uint8* m_image; int m_w; int m_h; int m_pitch; - Transaction(GSPng::Format fmt, const string& file, char* image, int w, int h, int pitch); + Transaction(GSPng::Format fmt, const string& file, const uint8* image, int w, int h, int pitch); ~Transaction(); }; - void Save(GSPng::Format fmt, const string& file, char* image, int w, int h, int pitch); + bool Save(GSPng::Format fmt, const string& file, uint8* image, int w, int h, int pitch, bool rb_swapped = false); class Worker : public GSJobQueue, 16 > { diff --git a/plugins/GSdx/GSTextureOGL.cpp b/plugins/GSdx/GSTextureOGL.cpp index c1fefd4b42..2eec972a51 100644 --- a/plugins/GSdx/GSTextureOGL.cpp +++ b/plugins/GSdx/GSTextureOGL.cpp @@ -530,7 +530,7 @@ bool GSTextureOGL::Save(const string& fn, bool dds) // Collect the texture data uint32 pitch = 4 * m_size.x; uint32 buf_size = pitch * m_size.y * 2;// Note *2 for security (depth/stencil) - char* image = (char*)malloc(buf_size); + std::unique_ptr image(new uint8[buf_size]); bool status = true; #ifdef ENABLE_OGL_DEBUG GSPng::Format fmt = GSPng::RGB_A_PNG; @@ -539,18 +539,18 @@ bool GSTextureOGL::Save(const string& fn, bool dds) #endif if (IsBackbuffer()) { - glReadPixels(0, 0, m_size.x, m_size.y, GL_RGBA, GL_UNSIGNED_BYTE, image); + glReadPixels(0, 0, m_size.x, m_size.y, GL_RGBA, GL_UNSIGNED_BYTE, image.get()); } else if(IsDss()) { glBindFramebuffer(GL_READ_FRAMEBUFFER, m_fbo_read); glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, m_texture_id, 0); - glReadPixels(0, 0, m_size.x, m_size.y, GL_DEPTH_COMPONENT, GL_UNSIGNED_INT, image); + glReadPixels(0, 0, m_size.x, m_size.y, GL_DEPTH_COMPONENT, GL_UNSIGNED_INT, image.get()); glBindFramebuffer(GL_READ_FRAMEBUFFER, 0); fmt = GSPng::DEPTH_PNG; } else if(m_format == GL_R32I) { - glGetTextureImage(m_texture_id, 0, GL_RED_INTEGER, GL_INT, buf_size, image); + glGetTextureImage(m_texture_id, 0, GL_RED_INTEGER, GL_INT, buf_size, image.get()); fmt = GSPng::R32I_PNG; @@ -563,11 +563,11 @@ bool GSTextureOGL::Save(const string& fn, bool dds) glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, m_texture_id, 0); if (m_format == GL_RGBA8) { - glReadPixels(0, 0, m_size.x, m_size.y, GL_RGBA, GL_UNSIGNED_BYTE, image); + glReadPixels(0, 0, m_size.x, m_size.y, GL_RGBA, GL_UNSIGNED_BYTE, image.get()); } else if (m_format == GL_R16UI) { - glReadPixels(0, 0, m_size.x, m_size.y, GL_RED_INTEGER, GL_UNSIGNED_SHORT, image); + glReadPixels(0, 0, m_size.x, m_size.y, GL_RED_INTEGER, GL_UNSIGNED_SHORT, image.get()); fmt = GSPng::R16I_PNG; // Not supported in Save function status = false; @@ -575,7 +575,7 @@ bool GSTextureOGL::Save(const string& fn, bool dds) else if (m_format == GL_R8) { fmt = GSPng::R8I_PNG; - glReadPixels(0, 0, m_size.x, m_size.y, GL_RED, GL_UNSIGNED_BYTE, image); + glReadPixels(0, 0, m_size.x, m_size.y, GL_RED, GL_UNSIGNED_BYTE, image.get()); // Not supported in Save function status = false; } @@ -584,11 +584,10 @@ bool GSTextureOGL::Save(const string& fn, bool dds) } #ifdef ENABLE_OGL_PNG - GSPng::Save(fmt, fn, image, m_size.x, m_size.y, pitch); + status = GSPng::Save(fmt, fn, image.get(), m_size.x, m_size.y, pitch); #else - if (status) Save(fn, image, pitch); + if (status) Save(fn, image.get(), pitch); #endif - free(image); return status; } diff --git a/plugins/GSdx/GSTextureSW.cpp b/plugins/GSdx/GSTextureSW.cpp index cdd75c27ad..119834a820 100644 --- a/plugins/GSdx/GSTextureSW.cpp +++ b/plugins/GSdx/GSTextureSW.cpp @@ -130,8 +130,7 @@ bool GSTextureSW::Save(const string& fn, bool dds) #else GSPng::Format fmt = GSPng::RGB_PNG; #endif - GSPng::Save(fmt, fn, (char*)m_data, m_size.x, m_size.y, m_pitch); - return true; + return GSPng::Save(fmt, fn, static_cast(m_data), m_size.x, m_size.y, m_pitch); #else if(FILE* fp = fopen(fn.c_str(), "wb")) From 38edd77034375c5974ce35b75b255d7d7a9c4dd3 Mon Sep 17 00:00:00 2001 From: Jonathan Li Date: Tue, 15 Dec 2015 19:18:59 +0000 Subject: [PATCH 2/9] gsdx-ogl:windows: Save screenshots as PNG files v2: Add zlib directory to property sheet --- plugins/GSdx/GSdx.vcxproj | 5 +++++ plugins/GSdx/GSdx.vcxproj.filters | 6 ++++++ plugins/GSdx/config.h | 2 +- plugins/GSdx/vsprops/common.props | 2 +- 4 files changed, 13 insertions(+), 2 deletions(-) diff --git a/plugins/GSdx/GSdx.vcxproj b/plugins/GSdx/GSdx.vcxproj index a2f9a673e3..ee2f82065f 100644 --- a/plugins/GSdx/GSdx.vcxproj +++ b/plugins/GSdx/GSdx.vcxproj @@ -692,6 +692,7 @@ + AssemblyAndSourceCode AssemblyAndSourceCode @@ -1980,6 +1981,7 @@ + @@ -2083,6 +2085,9 @@ + + {d6973076-9317-4ef2-a0b8-b7a18ac0713e} + {d80d4a75-c385-41bd-ae62-83d2e2b595a7} false diff --git a/plugins/GSdx/GSdx.vcxproj.filters b/plugins/GSdx/GSdx.vcxproj.filters index a150df0c37..8e086a0146 100644 --- a/plugins/GSdx/GSdx.vcxproj.filters +++ b/plugins/GSdx/GSdx.vcxproj.filters @@ -354,6 +354,9 @@ Source Files + + Source Files + @@ -716,6 +719,9 @@ Header Files + + Header Files + diff --git a/plugins/GSdx/config.h b/plugins/GSdx/config.h index 11a8f18ca3..2e091a5648 100644 --- a/plugins/GSdx/config.h +++ b/plugins/GSdx/config.h @@ -53,7 +53,7 @@ //#define ENABLE_OPENCL #endif -#if defined(__linux__) && PNGPP_SUPPORTED +#if (defined(__linux__) && PNGPP_SUPPORTED) || defined(_WIN32) // Allow to dump texture as PNG (require libpng++). It reduces the size of the dump // and alpha is well supported (on linux) #define ENABLE_OGL_PNG diff --git a/plugins/GSdx/vsprops/common.props b/plugins/GSdx/vsprops/common.props index 979dd01e85..0a13416da1 100644 --- a/plugins/GSdx/vsprops/common.props +++ b/plugins/GSdx/vsprops/common.props @@ -14,7 +14,7 @@ Level4 ProgramDatabase 4456;4458;4996;4995;4324;4100;4101;4201;4556;4127;4512;%(DisableSpecificWarnings) - $(DXSDK_DIR)include;$(VTUNE_AMPLIFIER_XE_2015_DIR)include;$(SolutionDir)3rdparty;$(SolutionDir)3rdparty\opencl;%(AdditionalIncludeDirectories) + $(DXSDK_DIR)include;$(VTUNE_AMPLIFIER_XE_2015_DIR)include;$(SolutionDir)3rdparty;$(SolutionDir)3rdparty\libpng;$(SolutionDir)3rdparty\opencl;$(SolutionDir)3rdparty\zlib;%(AdditionalIncludeDirectories) true From ee5861efc85f13a686361f839cb825add6ab2f9e Mon Sep 17 00:00:00 2001 From: Jonathan Li Date: Sun, 21 Feb 2016 10:49:48 +0000 Subject: [PATCH 3/9] gsdx: Remove non-PNG code It's no longer in use. --- plugins/GSdx/GSPng.cpp | 4 -- plugins/GSdx/GSTextureOGL.cpp | 121 +--------------------------------- plugins/GSdx/GSTextureOGL.h | 1 - plugins/GSdx/GSTextureSW.cpp | 91 ------------------------- plugins/GSdx/config.h | 6 -- 5 files changed, 1 insertion(+), 222 deletions(-) diff --git a/plugins/GSdx/GSPng.cpp b/plugins/GSdx/GSPng.cpp index 64654236d9..e31033f29c 100644 --- a/plugins/GSdx/GSPng.cpp +++ b/plugins/GSdx/GSPng.cpp @@ -106,7 +106,6 @@ namespace GSPng { bool Save(GSPng::Format fmt, const string& file, uint8* image, int w, int h, int pitch, bool rb_swapped) { -#ifdef ENABLE_OGL_PNG std::string root = file; root.replace(file.length() - 4, 4, ""); @@ -124,9 +123,6 @@ namespace GSPng { filename = root + pixel[fmt].extension[1]; return SaveFile(filename, fmt, image, row.get(), w, h, pitch); -#else - return false; -#endif } Transaction::Transaction(GSPng::Format fmt, const string& file, const uint8* image, int w, int h, int pitch) diff --git a/plugins/GSdx/GSTextureOGL.cpp b/plugins/GSdx/GSTextureOGL.cpp index 2eec972a51..22748c56e2 100644 --- a/plugins/GSdx/GSTextureOGL.cpp +++ b/plugins/GSdx/GSTextureOGL.cpp @@ -421,117 +421,12 @@ void GSTextureOGL::Unmap() { } -#ifndef _WIN32 - -#pragma pack(push, 1) - -struct BITMAPFILEHEADER -{ - uint16 bfType; - uint32 bfSize; - uint16 bfReserved1; - uint16 bfReserved2; - uint32 bfOffBits; -}; - -struct BITMAPINFOHEADER -{ - uint32 biSize; - int32 biWidth; - int32 biHeight; - uint16 biPlanes; - uint16 biBitCount; - uint32 biCompression; - uint32 biSizeImage; - int32 biXPelsPerMeter; - int32 biYPelsPerMeter; - uint32 biClrUsed; - uint32 biClrImportant; -}; - -#define BI_RGB 0 - -#pragma pack(pop) - -#endif -void GSTextureOGL::Save(const string& fn, const void* image, uint32 pitch) -{ - // Build a BMP file - FILE* fp = fopen(fn.c_str(), "wb"); - if (fp == NULL) - return; - - BITMAPINFOHEADER bih; - - memset(&bih, 0, sizeof(bih)); - - bih.biSize = sizeof(bih); - bih.biWidth = m_size.x; - bih.biHeight = m_size.y; - bih.biPlanes = 1; - bih.biBitCount = 32; - bih.biCompression = BI_RGB; - bih.biSizeImage = m_size.x * m_size.y << 2; - - BITMAPFILEHEADER bfh; - - memset(&bfh, 0, sizeof(bfh)); - - uint8* bfType = (uint8*)&bfh.bfType; - - // bfh.bfType = 'MB'; - bfType[0] = 0x42; - bfType[1] = 0x4d; - bfh.bfOffBits = sizeof(bfh) + sizeof(bih); - bfh.bfSize = bfh.bfOffBits + bih.biSizeImage; - bfh.bfReserved1 = bfh.bfReserved2 = 0; - - fwrite(&bfh, 1, sizeof(bfh), fp); - fwrite(&bih, 1, sizeof(bih), fp); - - uint8* data = (uint8*)image + (m_size.y - 1) * pitch; - - for(int h = m_size.y; h > 0; h--, data -= pitch) - { - if (false && IsDss()) { - // Only get the depth and convert it to an integer - uint8* better_data = data; - for (int w = m_size.x; w > 0; w--, better_data += 8) { - float* input = (float*)better_data; - // FIXME how to dump 32 bits value into 8bits component color - GLuint depth_integer = (GLuint)(*input * (float)UINT_MAX); - uint8 r = (depth_integer >> 0) & 0xFF; - uint8 g = (depth_integer >> 8) & 0xFF; - uint8 b = (depth_integer >> 16) & 0xFF; - uint8 a = (depth_integer >> 24) & 0xFF; - - fwrite(&r, 1, 1, fp); - fwrite(&g, 1, 1, fp); - fwrite(&b, 1, 1, fp); - fwrite(&a, 1, 1, fp); - } - } else { - // swap red and blue - uint8* better_data = data; - for (int w = m_size.x; w > 0; w--, better_data += 4) { - uint8 red = better_data[2]; - better_data[2] = better_data[0]; - better_data[0] = red; - fwrite(better_data, 1, 4, fp); - } - } - } - - fclose(fp); -} - bool GSTextureOGL::Save(const string& fn, bool dds) { // Collect the texture data uint32 pitch = 4 * m_size.x; uint32 buf_size = pitch * m_size.y * 2;// Note *2 for security (depth/stencil) std::unique_ptr image(new uint8[buf_size]); - bool status = true; #ifdef ENABLE_OGL_DEBUG GSPng::Format fmt = GSPng::RGB_A_PNG; #else @@ -553,10 +448,6 @@ bool GSTextureOGL::Save(const string& fn, bool dds) glGetTextureImage(m_texture_id, 0, GL_RED_INTEGER, GL_INT, buf_size, image.get()); fmt = GSPng::R32I_PNG; - - // Not supported in Save function - status = false; - } else { glBindFramebuffer(GL_READ_FRAMEBUFFER, m_fbo_read); @@ -569,27 +460,17 @@ bool GSTextureOGL::Save(const string& fn, bool dds) { glReadPixels(0, 0, m_size.x, m_size.y, GL_RED_INTEGER, GL_UNSIGNED_SHORT, image.get()); fmt = GSPng::R16I_PNG; - // Not supported in Save function - status = false; } else if (m_format == GL_R8) { fmt = GSPng::R8I_PNG; glReadPixels(0, 0, m_size.x, m_size.y, GL_RED, GL_UNSIGNED_BYTE, image.get()); - // Not supported in Save function - status = false; } glBindFramebuffer(GL_READ_FRAMEBUFFER, 0); } -#ifdef ENABLE_OGL_PNG - status = GSPng::Save(fmt, fn, image.get(), m_size.x, m_size.y, pitch); -#else - if (status) Save(fn, image.get(), pitch); -#endif - - return status; + return GSPng::Save(fmt, fn, image.get(), m_size.x, m_size.y, pitch); } uint32 GSTextureOGL::GetMemUsage() diff --git a/plugins/GSdx/GSTextureOGL.h b/plugins/GSdx/GSTextureOGL.h index 1050907273..40e0af88c5 100644 --- a/plugins/GSdx/GSTextureOGL.h +++ b/plugins/GSdx/GSTextureOGL.h @@ -64,7 +64,6 @@ class GSTextureOGL : public GSTexture bool Map(GSMap& m, const GSVector4i* r = NULL); void Unmap(); bool Save(const string& fn, bool dds = false); - void Save(const string& fn, const void* image, uint32 pitch); bool IsBackbuffer() { return (m_type == GSTexture::Backbuffer); } bool IsDss() { return (m_type == GSTexture::DepthStencil); } diff --git a/plugins/GSdx/GSTextureSW.cpp b/plugins/GSdx/GSTextureSW.cpp index 119834a820..b2ba42e3d4 100644 --- a/plugins/GSdx/GSTextureSW.cpp +++ b/plugins/GSdx/GSTextureSW.cpp @@ -85,105 +85,14 @@ void GSTextureSW::Unmap() m_mapped.clear(); } -#ifndef _WIN32 - -#pragma pack(push, 1) - -struct BITMAPFILEHEADER -{ - uint16 bfType; - uint32 bfSize; - uint16 bfReserved1; - uint16 bfReserved2; - uint32 bfOffBits; -}; - -struct BITMAPINFOHEADER -{ - uint32 biSize; - int32 biWidth; - int32 biHeight; - uint16 biPlanes; - uint16 biBitCount; - uint32 biCompression; - uint32 biSizeImage; - int32 biXPelsPerMeter; - int32 biYPelsPerMeter; - uint32 biClrUsed; - uint32 biClrImportant; -}; - -#define BI_RGB 0 - -#pragma pack(pop) - -#endif - bool GSTextureSW::Save(const string& fn, bool dds) { if(dds) return false; // not implemented -#ifdef ENABLE_OGL_PNG - #ifdef ENABLE_OGL_DEBUG GSPng::Format fmt = GSPng::RGB_A_PNG; #else GSPng::Format fmt = GSPng::RGB_PNG; #endif return GSPng::Save(fmt, fn, static_cast(m_data), m_size.x, m_size.y, m_pitch); - -#else - if(FILE* fp = fopen(fn.c_str(), "wb")) - { - BITMAPINFOHEADER bih; - - memset(&bih, 0, sizeof(bih)); - - bih.biSize = sizeof(bih); - bih.biWidth = m_size.x; - bih.biHeight = m_size.y; - bih.biPlanes = 1; - bih.biBitCount = 32; - bih.biCompression = BI_RGB; - bih.biSizeImage = m_size.x * m_size.y << 2; - - BITMAPFILEHEADER bfh; - - memset(&bfh, 0, sizeof(bfh)); - - uint8* bfType = (uint8*)&bfh.bfType; - - // bfh.bfType = 'MB'; - bfType[0] = 0x42; - bfType[1] = 0x4d; - bfh.bfOffBits = sizeof(bfh) + sizeof(bih); - bfh.bfSize = bfh.bfOffBits + bih.biSizeImage; - bfh.bfReserved1 = bfh.bfReserved2 = 0; - - fwrite(&bfh, 1, sizeof(bfh), fp); - fwrite(&bih, 1, sizeof(bih), fp); - - uint8* data = (uint8*)m_data + (m_size.y - 1) * m_pitch; - - for(int h = m_size.y; h > 0; h--, data -= m_pitch) - { - for(int i = 0; i < m_size.x; i++) - { - uint32 c = ((uint32*)data)[i]; - - c = (c & 0xff00ff00) | ((c & 0x00ff0000) >> 16) | ((c & 0x000000ff) << 16); - - fwrite(&c, 1, sizeof(c), fp); - } - - // fwrite(data, 1, m_size.x << 2, fp); // TODO: swap red-blue? - } - - fclose(fp); - - return true; - } - - return false; -#endif } diff --git a/plugins/GSdx/config.h b/plugins/GSdx/config.h index 2e091a5648..9527c023c9 100644 --- a/plugins/GSdx/config.h +++ b/plugins/GSdx/config.h @@ -52,9 +52,3 @@ #ifdef _WIN32 //#define ENABLE_OPENCL #endif - -#if (defined(__linux__) && PNGPP_SUPPORTED) || defined(_WIN32) -// Allow to dump texture as PNG (require libpng++). It reduces the size of the dump -// and alpha is well supported (on linux) -#define ENABLE_OGL_PNG -#endif From 97215008c9ca8c33c93f4d7a6159c45dba0270de Mon Sep 17 00:00:00 2001 From: Jonathan Li Date: Sun, 21 Feb 2016 10:50:55 +0000 Subject: [PATCH 4/9] ci|cmake|debian: Remove png++ build dependencies GSdx now uses libpng directly. png++ is now unused, so let's remove it. --- cmake/SearchForStuff.cmake | 1 - cmake/SelectPcsx2Plugins.cmake | 2 +- debian-packager/control | 1 - plugins/GSdx/CMakeLists.txt | 4 ---- travis.sh | 5 ----- 5 files changed, 1 insertion(+), 12 deletions(-) diff --git a/cmake/SearchForStuff.cmake b/cmake/SearchForStuff.cmake index f33c5c1588..89b7f9875f 100644 --- a/cmake/SearchForStuff.cmake +++ b/cmake/SearchForStuff.cmake @@ -74,7 +74,6 @@ if(EGL_API) endif() check_lib(PORTAUDIO portaudio portaudio.h pa_linux_alsa.h) check_lib(SOUNDTOUCH SoundTouch soundtouch/SoundTouch.h) -check_lib(PNGPP FALSE png++/png.hpp) if(SDL2_API) check_lib(SDL2 SDL2 SDL.h PATH_SUFFIXES SDL2) diff --git a/cmake/SelectPcsx2Plugins.cmake b/cmake/SelectPcsx2Plugins.cmake index 6ceb22aef6..5f1c3d7063 100644 --- a/cmake/SelectPcsx2Plugins.cmake +++ b/cmake/SelectPcsx2Plugins.cmake @@ -5,7 +5,7 @@ set(msg_dep_common_libs "check these libraries -> wxWidgets (>=2.8.10), aio") set(msg_dep_pcsx2 "check these libraries -> wxWidgets (>=2.8.10), gtk2 (>=2.16), zlib (>=1.2.4), pcsx2 common libs") set(msg_dep_cdvdiso "check these libraries -> bzip2 (>=1.0.5), gtk2 (>=2.16)") set(msg_dep_zerogs "check these libraries -> glew (>=1.6), opengl, X11, nvidia-cg-toolkit (>=2.1)") -set(msg_dep_gsdx "check these libraries -> opengl, png++, X11") +set(msg_dep_gsdx "check these libraries -> opengl, png (>=1.2), zlib (>=1.2.4), X11") set(msg_dep_onepad "check these libraries -> sdl (>=1.2), X11, gtk2 (>=2.16)") set(msg_dep_spu2x "check these libraries -> soundtouch (>=1.5), alsa, portaudio (>=1.9), sdl (>=1.2) pcsx2 common libs") set(msg_dep_zerospu2 "check these libraries -> soundtouch (>=1.5), alsa") diff --git a/debian-packager/control b/debian-packager/control index a38c91313e..dae209dd89 100644 --- a/debian-packager/control +++ b/debian-packager/control @@ -12,7 +12,6 @@ Build-Depends: cmake (>= 2.8.5), libglu1-mesa-dev, libgtk2.0-dev (>= 2.16), libpng12-dev, - libpng++-dev, libpulse-dev, libsdl2-dev, libsoundtouch-dev, diff --git a/plugins/GSdx/CMakeLists.txt b/plugins/GSdx/CMakeLists.txt index 55703500d0..7309ac7d9a 100644 --- a/plugins/GSdx/CMakeLists.txt +++ b/plugins/GSdx/CMakeLists.txt @@ -31,10 +31,6 @@ if(LIBLZMA_FOUND) set(GSdxFinalFlags ${GSdxFinalFlags} -DLZMA_SUPPORTED) endif() -if(PNGPP_FOUND) - set(GSdxFinalFlags ${GSdxFinalFlags} -DPNGPP_SUPPORTED) -endif() - #Clang doesn't support a few common flags that GCC does. if(NOT USE_CLANG) set(GSdxFinalFlags ${GSdxFinalFlags} -fabi-version=6) diff --git a/travis.sh b/travis.sh index 10c3fc3c2c..13ce7be72c 100755 --- a/travis.sh +++ b/travis.sh @@ -48,10 +48,6 @@ linux_32_before_install() { portaudio19-dev:i386 \ zlib1g-dev:i386 \ ${COMPILER_PACKAGE} - - # libpng++-dev is noarch but doesn't install nicely. - apt-get download libpng++-dev - sudo dpkg --force-all -i $(ls | grep 'libpng++-dev') } linux_32_script() { @@ -91,7 +87,6 @@ linux_64_before_install() { libaio-dev \ libasound2-dev \ libgtk2.0-dev \ - libpng++-dev \ libpng12-dev \ libsdl2-dev \ libsoundtouch-dev \ From aa3f4c74dcddca1277dc7cf8b83c4c786a12cc5b Mon Sep 17 00:00:00 2001 From: Jonathan Li Date: Wed, 16 Dec 2015 19:39:40 +0000 Subject: [PATCH 5/9] GSDumpGUI: Add png image display support --- tools/GSDumpGUI/Forms/frmMain.cs | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/tools/GSDumpGUI/Forms/frmMain.cs b/tools/GSDumpGUI/Forms/frmMain.cs index 2fa5c8aeab..12a9dfc43c 100644 --- a/tools/GSDumpGUI/Forms/frmMain.cs +++ b/tools/GSDumpGUI/Forms/frmMain.cs @@ -293,19 +293,22 @@ namespace GSDumpGUI { if (lstDumps.SelectedIndex != -1) { + String [] Extensions = new String[] { ".png", ".bmp" }; String DumpFileName = lstDumps.SelectedItem.ToString().Split(new char[] { '|' })[0]; String Filename = Path.GetDirectoryName(Properties.Settings.Default.DumpDir + "\\") + - "\\" + Path.GetFileNameWithoutExtension(DumpFileName) + ".bmp"; - if (File.Exists(Filename)) + "\\" + Path.GetFileNameWithoutExtension(DumpFileName); + + foreach (String Extension in Extensions) { - pctBox.Image = Image.FromFile(Filename); - pctBox.Cursor = Cursors.Hand; - } - else - { - pctBox.Image = NoImage; - pctBox.Cursor = Cursors.Default; + if (File.Exists(Filename + Extension)) + { + pctBox.Load(Filename + Extension); + pctBox.Cursor = Cursors.Hand; + return; + } } + pctBox.Image = NoImage; + pctBox.Cursor = Cursors.Default; } } @@ -313,10 +316,7 @@ namespace GSDumpGUI { if (pctBox.Cursor == Cursors.Hand) { - String DumpFileName = lstDumps.SelectedItem.ToString().Split(new char[] { '|' })[0]; - String Filename = Path.GetDirectoryName(Properties.Settings.Default.DumpDir + "\\") + - "\\" + Path.GetFileNameWithoutExtension(DumpFileName) + ".bmp"; - Process.Start(Filename); + Process.Start(pctBox.ImageLocation); } } From cf939620eabca1ae8afd93a7971d782e5c62fd7b Mon Sep 17 00:00:00 2001 From: Jonathan Li Date: Mon, 22 Feb 2016 19:32:57 +0000 Subject: [PATCH 6/9] gsdx-ogl: Save depth image as RGB and alpha images --- plugins/GSdx/GSPng.cpp | 1 - plugins/GSdx/GSPng.h | 1 - plugins/GSdx/GSTextureOGL.cpp | 2 +- 3 files changed, 1 insertion(+), 3 deletions(-) diff --git a/plugins/GSdx/GSPng.cpp b/plugins/GSdx/GSPng.cpp index e31033f29c..94401b0e48 100644 --- a/plugins/GSdx/GSPng.cpp +++ b/plugins/GSdx/GSPng.cpp @@ -34,7 +34,6 @@ struct { {PNG_COLOR_TYPE_RGB , 4, 3, 8 , {".png", nullptr}}, // RGB_PNG {PNG_COLOR_TYPE_RGB , 4, 3, 8 , {".png", "_alpha.png"}}, // RGB_A_PNG {PNG_COLOR_TYPE_GRAY, 4, 1, 8 , {"_alpha.png", nullptr}}, // ALPHA_PNG - {PNG_COLOR_TYPE_GRAY, 4, 2, 16, {"_lsb.png", "_msb.png"}}, // DEPTH_PNG {PNG_COLOR_TYPE_GRAY, 1, 1, 8 , {"_R8I.png", nullptr}}, // R8I_PNG {PNG_COLOR_TYPE_GRAY, 2, 2, 16, {"_R16I.png", nullptr}}, // R16I_PNG {PNG_COLOR_TYPE_GRAY, 4, 2, 16, {"_R32I_lsb.png", "_R32I_msb.png"}}, // R32I_PNG diff --git a/plugins/GSdx/GSPng.h b/plugins/GSdx/GSPng.h index db881e7f47..2e0874814a 100644 --- a/plugins/GSdx/GSPng.h +++ b/plugins/GSdx/GSPng.h @@ -29,7 +29,6 @@ namespace GSPng { RGB_PNG, RGB_A_PNG, ALPHA_PNG, - DEPTH_PNG, R8I_PNG, R16I_PNG, R32I_PNG, diff --git a/plugins/GSdx/GSTextureOGL.cpp b/plugins/GSdx/GSTextureOGL.cpp index 22748c56e2..77c6302247 100644 --- a/plugins/GSdx/GSTextureOGL.cpp +++ b/plugins/GSdx/GSTextureOGL.cpp @@ -443,7 +443,7 @@ bool GSTextureOGL::Save(const string& fn, bool dds) glBindFramebuffer(GL_READ_FRAMEBUFFER, 0); - fmt = GSPng::DEPTH_PNG; + fmt = GSPng::RGB_A_PNG; } else if(m_format == GL_R32I) { glGetTextureImage(m_texture_id, 0, GL_RED_INTEGER, GL_INT, buf_size, image.get()); From d865efceb9ae1e018f0bbcc5c561e796a9c3c382 Mon Sep 17 00:00:00 2001 From: Jonathan Li Date: Fri, 19 Feb 2016 18:05:11 +0000 Subject: [PATCH 7/9] gsdx-d3d11: Save screenshots as PNG files --- plugins/GSdx/GSTexture11.cpp | 100 ++++++++++++++++++++++++----------- 1 file changed, 68 insertions(+), 32 deletions(-) diff --git a/plugins/GSdx/GSTexture11.cpp b/plugins/GSdx/GSTexture11.cpp index 45a17b205c..4ec6d29804 100644 --- a/plugins/GSdx/GSTexture11.cpp +++ b/plugins/GSdx/GSTexture11.cpp @@ -21,6 +21,7 @@ #include "stdafx.h" #include "GSTexture11.h" +#include "GSPng.h" GSTexture11::GSTexture11(ID3D11Texture2D* texture) : m_texture(texture) @@ -94,60 +95,95 @@ void GSTexture11::Unmap() bool GSTexture11::Save(const string& fn, bool dds) { - CComPtr res; + CComPtr res; + D3D11_TEXTURE2D_DESC desc; - if(m_desc.BindFlags & D3D11_BIND_DEPTH_STENCIL) + m_texture->GetDesc(&desc); + + desc.Usage = D3D11_USAGE_STAGING; + desc.BindFlags = 0; + desc.CPUAccessFlags = D3D11_CPU_ACCESS_READ; + + HRESULT hr = m_dev->CreateTexture2D(&desc, nullptr, &res); + if (FAILED(hr)) { - HRESULT hr; + return false; + } - D3D11_TEXTURE2D_DESC desc; + m_ctx->CopyResource(res, m_texture); - memset(&desc, 0, sizeof(desc)); - - m_texture->GetDesc(&desc); - - desc.Usage = D3D11_USAGE_STAGING; - desc.BindFlags = 0; - desc.CPUAccessFlags = D3D11_CPU_ACCESS_READ; - - CComPtr src, dst; - - hr = m_dev->CreateTexture2D(&desc, NULL, &src); - - m_ctx->CopyResource(src, m_texture); + if (m_desc.BindFlags & D3D11_BIND_DEPTH_STENCIL) + { + CComPtr dst; desc.Format = DXGI_FORMAT_R8G8B8A8_UNORM; - desc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE; + desc.CPUAccessFlags |= D3D11_CPU_ACCESS_WRITE; - hr = m_dev->CreateTexture2D(&desc, NULL, &dst); + hr = m_dev->CreateTexture2D(&desc, nullptr, &dst); + if (FAILED(hr)) + { + return false; + } D3D11_MAPPED_SUBRESOURCE sm, dm; - hr = m_ctx->Map(src, 0, D3D11_MAP_READ, 0, &sm); - hr = m_ctx->Map(dst, 0, D3D11_MAP_WRITE, 0, &dm); - - uint8* s = (uint8*)sm.pData; - uint8* d = (uint8*)dm.pData; - - for(uint32 y = 0; y < desc.Height; y++, s += sm.RowPitch, d += dm.RowPitch) + hr = m_ctx->Map(res, 0, D3D11_MAP_READ, 0, &sm); + if (FAILED(hr)) { - for(uint32 x = 0; x < desc.Width; x++) + return false; + } + hr = m_ctx->Map(dst, 0, D3D11_MAP_WRITE, 0, &dm); + if (FAILED(hr)) + { + m_ctx->Unmap(res, 0); + return false; + } + + uint8* s = static_cast(sm.pData); + uint8* d = static_cast(dm.pData); + + for (uint32 y = 0; y < desc.Height; y++, s += sm.RowPitch, d += dm.RowPitch) + { + for (uint32 x = 0; x < desc.Width; x++) { - ((uint32*)d)[x] = (uint32)(ldexpf(((float*)s)[x*2], 32)); + reinterpret_cast(d)[x] = static_cast(ldexpf(reinterpret_cast(s)[x*2], 32)); } } - m_ctx->Unmap(src, 0); + m_ctx->Unmap(res, 0); m_ctx->Unmap(dst, 0); res = dst; } - else + + res->GetDesc(&desc); + + GSPng::Format format; + switch (desc.Format) { - res = m_texture; + case DXGI_FORMAT_A8_UNORM: + format = GSPng::R8I_PNG; + break; + case DXGI_FORMAT_R8G8B8A8_UNORM: + format = dds ? GSPng::RGBA_PNG : (m_desc.BindFlags & D3D11_BIND_DEPTH_STENCIL ? GSPng::RGB_A_PNG : GSPng::RGB_PNG); + break; + default: + fprintf(stderr, "DXGI_FORMAT %d not saved to image\n", desc.Format); + return false; } - return SUCCEEDED(D3DX11SaveTextureToFile(m_ctx, res, dds ? D3DX11_IFF_DDS : D3DX11_IFF_BMP, fn.c_str())); + D3D11_MAPPED_SUBRESOURCE sm; + hr = m_ctx->Map(res, 0, D3D11_MAP_READ, 0, &sm); + if (FAILED(hr)) + { + return false; + } + + bool success = GSPng::Save(format, fn, static_cast(sm.pData), desc.Width, desc.Height, sm.RowPitch); + + m_ctx->Unmap(res, 0); + + return success; } GSTexture11::operator ID3D11Texture2D*() From a97cbf038cef094ab778e293ea816561b968795f Mon Sep 17 00:00:00 2001 From: Jonathan Li Date: Fri, 19 Feb 2016 18:05:39 +0000 Subject: [PATCH 8/9] gsdx-d3d9: Save screenshots as PNG files v2: Avoid unnecessary memory copy --- plugins/GSdx/GSTexture9.cpp | 86 +++++++++++++++++++++---------------- 1 file changed, 49 insertions(+), 37 deletions(-) diff --git a/plugins/GSdx/GSTexture9.cpp b/plugins/GSdx/GSTexture9.cpp index ecf24195c9..6c09e3c7c7 100644 --- a/plugins/GSdx/GSTexture9.cpp +++ b/plugins/GSdx/GSTexture9.cpp @@ -21,6 +21,7 @@ #include "stdafx.h" #include "GSTexture9.h" +#include "GSPng.h" GSTexture9::GSTexture9(IDirect3DSurface9* surface) { @@ -143,56 +144,67 @@ void GSTexture9::Unmap() bool GSTexture9::Save(const string& fn, bool dds) { + bool rb_swapped = true; CComPtr surface; - if(m_desc.Usage & D3DUSAGE_DEPTHSTENCIL) + D3DSURFACE_DESC desc; + m_surface->GetDesc(&desc); + + if (m_desc.Usage & D3DUSAGE_DEPTHSTENCIL && desc.Format != D3DFMT_D32F_LOCKABLE) { - HRESULT hr; + return false; + } - D3DSURFACE_DESC desc; - - m_surface->GetDesc(&desc); - - if(desc.Format != D3DFMT_D32F_LOCKABLE) - return false; - - hr = m_dev->CreateOffscreenPlainSurface(desc.Width, desc.Height, D3DFMT_A8R8G8B8, D3DPOOL_SYSTEMMEM, &surface, NULL); - - D3DLOCKED_RECT slr, dlr; - - hr = m_surface->LockRect(&slr, NULL, 0); - hr = surface->LockRect(&dlr, NULL, 0); - - uint8* s = (uint8*)slr.pBits; - uint8* d = (uint8*)dlr.pBits; - - for(uint32 y = 0; y < desc.Height; y++, s += slr.Pitch, d += dlr.Pitch) - { - for(uint32 x = 0; x < desc.Width; x++) - { - ((float*)d)[x] = ((float*)s)[x]; - } - } - - m_surface->UnlockRect(); - surface->UnlockRect(); + if (desc.Format == D3DFMT_A8 || desc.Pool == D3DPOOL_MANAGED || desc.Usage == D3DUSAGE_DEPTHSTENCIL) + { + surface = m_surface; + rb_swapped = false; } else { - surface = m_surface; + HRESULT hr; + + hr = m_dev->CreateOffscreenPlainSurface(desc.Width, desc.Height, desc.Format, D3DPOOL_SYSTEMMEM, &surface, nullptr); + if (FAILED(hr)) + { + return false; + } + + hr = m_dev->GetRenderTargetData(m_surface, surface); + if (FAILED(hr)) + { + return false; + } } - if(surface != NULL) + GSPng::Format format; + switch (desc.Format) { - return SUCCEEDED(D3DXSaveSurfaceToFile(fn.c_str(), dds ? D3DXIFF_DDS : D3DXIFF_BMP, surface, NULL, NULL)); + case D3DFMT_A8: + format = GSPng::R8I_PNG; + break; + case D3DFMT_A8R8G8B8: + format = dds? GSPng::RGBA_PNG : GSPng::RGB_PNG; + break; + case D3DFMT_D32F_LOCKABLE: + format = GSPng::RGB_A_PNG; + break; + default: + fprintf(stderr, "D3DFMT %d not saved to image\n", desc.Format); + return false; } -/* - if(CComQIPtr texture = surface) + + D3DLOCKED_RECT slr; + HRESULT hr = surface->LockRect(&slr, nullptr, 0); + if (FAILED(hr)) { - return SUCCEEDED(D3DXSaveTextureToFile(fn.c_str(), dds ? D3DXIFF_DDS : D3DXIFF_BMP, texture, NULL)); + return false; } -*/ - return false; + + bool success = GSPng::Save(format, fn, static_cast(slr.pBits), desc.Width, desc.Height, slr.Pitch, rb_swapped); + + surface->UnlockRect(); + return success; } GSTexture9::operator IDirect3DSurface9*() From 6b950d0408a6fc7c12a070c277e48e0da4e60aaf Mon Sep 17 00:00:00 2001 From: Jonathan Li Date: Wed, 24 Feb 2016 21:52:17 +0000 Subject: [PATCH 9/9] gsdx: Allow dump and capture PNG compression to be changed Valid values for png_compression_level are from 0 (no compression) to 9 (max compression). The default is 1. v2: Use zlib Z_BEST_SPEED (1) and Z_BEST_COMPRESSION (9) defines. --- plugins/GSdx/GSCapture.cpp | 6 ++++-- plugins/GSdx/GSCapture.h | 1 + plugins/GSdx/GSPng.cpp | 22 ++++++++++++---------- plugins/GSdx/GSPng.h | 5 +++-- plugins/GSdx/GSRenderer.cpp | 2 +- plugins/GSdx/GSRendererHW.cpp | 4 ++-- plugins/GSdx/GSTexture.h | 2 +- plugins/GSdx/GSTexture11.cpp | 5 +++-- plugins/GSdx/GSTexture11.h | 2 +- plugins/GSdx/GSTexture9.cpp | 5 +++-- plugins/GSdx/GSTexture9.h | 2 +- plugins/GSdx/GSTextureNull.h | 2 +- plugins/GSdx/GSTextureOGL.cpp | 5 +++-- plugins/GSdx/GSTextureOGL.h | 2 +- plugins/GSdx/GSTextureSW.cpp | 5 +++-- plugins/GSdx/GSTextureSW.h | 2 +- plugins/GSdx/stdafx.h | 2 ++ 17 files changed, 43 insertions(+), 31 deletions(-) diff --git a/plugins/GSdx/GSCapture.cpp b/plugins/GSdx/GSCapture.cpp index 75cd0c1066..7e85772f64 100644 --- a/plugins/GSdx/GSCapture.cpp +++ b/plugins/GSdx/GSCapture.cpp @@ -488,6 +488,8 @@ bool GSCapture::BeginCapture(float fps, GSVector2i recomendedResolution, float a m_size.x = theApp.GetConfig("capture_resx", 1280); m_size.y = theApp.GetConfig("capture_resy", 1024); + m_compression_level = theApp.GetConfig("png_compression_level", Z_BEST_SPEED); + #ifdef __linux__ for(int i = 0; i < m_threads; i++) { m_workers.push_back(new GSPng::Worker()); @@ -524,8 +526,8 @@ bool GSCapture::DeliverFrame(const void* bits, int pitch, bool rgba) #elif __linux__ std::string out_file = m_out_dir + format("/frame.%010d.png", m_frame); - //GSPng::Save(GSPng::RGB_PNG, out_file, (uint8*)bits, m_size.x, m_size.y, pitch); - m_workers[m_frame%m_threads]->Push(shared_ptr(new GSPng::Transaction(GSPng::RGB_PNG, out_file, static_cast(bits), m_size.x, m_size.y, pitch))); + //GSPng::Save(GSPng::RGB_PNG, out_file, (uint8*)bits, m_size.x, m_size.y, pitch, m_compression_level); + m_workers[m_frame%m_threads]->Push(shared_ptr(new GSPng::Transaction(GSPng::RGB_PNG, out_file, static_cast(bits), m_size.x, m_size.y, pitch, m_compression_level))); m_frame++; diff --git a/plugins/GSdx/GSCapture.h b/plugins/GSdx/GSCapture.h index 49cbd8f2c1..3fdb7afde1 100644 --- a/plugins/GSdx/GSCapture.h +++ b/plugins/GSdx/GSCapture.h @@ -45,6 +45,7 @@ class GSCapture #elif __linux__ vector m_workers; + int m_compression_level; #endif diff --git a/plugins/GSdx/GSPng.cpp b/plugins/GSdx/GSPng.cpp index 94401b0e48..62b7e7d4b5 100644 --- a/plugins/GSdx/GSPng.cpp +++ b/plugins/GSdx/GSPng.cpp @@ -42,7 +42,7 @@ struct { namespace GSPng { bool SaveFile(const string& file, Format fmt, uint8* image, uint8* row, - int width, int height, int pitch, + int width, int height, int pitch, int compression, bool rb_swapped = false, bool first_image = false) { int channel_bit_depth = pixel[fmt].channel_bit_depth; @@ -71,8 +71,11 @@ namespace GSPng { if (setjmp(png_jmpbuf(png_ptr))) throw GSDXRecoverableError(); + if (compression < 0 || compression > Z_BEST_COMPRESSION) + compression = Z_BEST_SPEED; + png_init_io(png_ptr, fp); - png_set_compression_level(png_ptr, Z_BEST_COMPRESSION); + png_set_compression_level(png_ptr, compression); png_set_IHDR(png_ptr, info_ptr, width, height, channel_bit_depth, type, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT); png_write_info(png_ptr, info_ptr); @@ -91,8 +94,7 @@ namespace GSPng { png_write_end(png_ptr, nullptr); success = true; - } - catch (GSDXRecoverableError&) { + } catch (GSDXRecoverableError&) { fprintf(stderr, "Failed to write image %s\n", file.c_str()); } @@ -103,7 +105,7 @@ namespace GSPng { return success; } - bool Save(GSPng::Format fmt, const string& file, uint8* image, int w, int h, int pitch, bool rb_swapped) + bool Save(GSPng::Format fmt, const string& file, uint8* image, int w, int h, int pitch, int compression, bool rb_swapped) { std::string root = file; root.replace(file.length() - 4, 4, ""); @@ -113,7 +115,7 @@ namespace GSPng { std::unique_ptr row(new uint8[pixel[fmt].bytes_per_pixel_out * w]); std::string filename = root + pixel[fmt].extension[0]; - if (!SaveFile(filename, fmt, image, row.get(), w, h, pitch, rb_swapped, true)) + if (!SaveFile(filename, fmt, image, row.get(), w, h, pitch, compression, rb_swapped, true)) return false; // Second image @@ -121,11 +123,11 @@ namespace GSPng { return true; filename = root + pixel[fmt].extension[1]; - return SaveFile(filename, fmt, image, row.get(), w, h, pitch); + return SaveFile(filename, fmt, image, row.get(), w, h, pitch, compression); } - Transaction::Transaction(GSPng::Format fmt, const string& file, const uint8* image, int w, int h, int pitch) - : m_fmt(fmt), m_file(file), m_w(w), m_h(h), m_pitch(pitch) + Transaction::Transaction(GSPng::Format fmt, const string& file, const uint8* image, int w, int h, int pitch, int compression) + : m_fmt(fmt), m_file(file), m_w(w), m_h(h), m_pitch(pitch), m_compression(compression) { // Note: yes it would be better to use shared pointer m_image = (uint8*)_aligned_malloc(pitch*h, 32); @@ -141,7 +143,7 @@ namespace GSPng { void Worker::Process(shared_ptr& item) { - Save(item->m_fmt, item->m_file, item->m_image, item->m_w, item->m_h, item->m_pitch); + Save(item->m_fmt, item->m_file, item->m_image, item->m_w, item->m_h, item->m_pitch, item->m_compression); } } diff --git a/plugins/GSdx/GSPng.h b/plugins/GSdx/GSPng.h index 2e0874814a..6dd623fee3 100644 --- a/plugins/GSdx/GSPng.h +++ b/plugins/GSdx/GSPng.h @@ -44,12 +44,13 @@ namespace GSPng { int m_w; int m_h; int m_pitch; + int m_compression; - Transaction(GSPng::Format fmt, const string& file, const uint8* image, int w, int h, int pitch); + Transaction(GSPng::Format fmt, const string& file, const uint8* image, int w, int h, int pitch, int compression); ~Transaction(); }; - bool Save(GSPng::Format fmt, const string& file, uint8* image, int w, int h, int pitch, bool rb_swapped = false); + bool Save(GSPng::Format fmt, const string& file, uint8* image, int w, int h, int pitch, int compression, bool rb_swapped = false); class Worker : public GSJobQueue, 16 > { diff --git a/plugins/GSdx/GSRenderer.cpp b/plugins/GSdx/GSRenderer.cpp index f2c084a0a6..6b1b8eac46 100644 --- a/plugins/GSdx/GSRenderer.cpp +++ b/plugins/GSdx/GSRenderer.cpp @@ -469,7 +469,7 @@ void GSRenderer::VSync(int field) if(GSTexture* t = m_dev->GetCurrent()) { - t->Save(m_snapshot + ".bmp"); + t->Save(m_snapshot + ".bmp", true); } m_snapshot.clear(); diff --git a/plugins/GSdx/GSRendererHW.cpp b/plugins/GSdx/GSRendererHW.cpp index 36857d208b..e877ca28b2 100644 --- a/plugins/GSdx/GSRendererHW.cpp +++ b/plugins/GSdx/GSRendererHW.cpp @@ -445,13 +445,13 @@ void GSRendererHW::Draw() (int)context->CLAMP.MINU, (int)context->CLAMP.MAXU, (int)context->CLAMP.MINV, (int)context->CLAMP.MAXV); - tex->m_texture->Save(root_hw+s, true); + tex->m_texture->Save(root_hw+s, false, true); if(tex->m_palette) { s = format("%05d_f%lld_tpx_%05x_%d.dds", s_n, frame, context->TEX0.CBP, context->TEX0.CPSM); - tex->m_palette->Save(root_hw+s, true); + tex->m_palette->Save(root_hw+s, false, true); } } diff --git a/plugins/GSdx/GSTexture.h b/plugins/GSdx/GSTexture.h index 4b2626d25b..5418cfebbb 100644 --- a/plugins/GSdx/GSTexture.h +++ b/plugins/GSdx/GSTexture.h @@ -46,7 +46,7 @@ public: virtual bool Update(const GSVector4i& r, const void* data, int pitch) = 0; virtual bool Map(GSMap& m, const GSVector4i* r = NULL) = 0; virtual void Unmap() = 0; - virtual bool Save(const string& fn, bool dds = false) = 0; + virtual bool Save(const string& fn, bool user_image = false, bool dds = false) = 0; virtual void Invalidate() {} virtual uint32 GetID() { return 0; } diff --git a/plugins/GSdx/GSTexture11.cpp b/plugins/GSdx/GSTexture11.cpp index 4ec6d29804..2e4cbf557b 100644 --- a/plugins/GSdx/GSTexture11.cpp +++ b/plugins/GSdx/GSTexture11.cpp @@ -93,7 +93,7 @@ void GSTexture11::Unmap() } } -bool GSTexture11::Save(const string& fn, bool dds) +bool GSTexture11::Save(const string& fn, bool user_image, bool dds) { CComPtr res; D3D11_TEXTURE2D_DESC desc; @@ -179,7 +179,8 @@ bool GSTexture11::Save(const string& fn, bool dds) return false; } - bool success = GSPng::Save(format, fn, static_cast(sm.pData), desc.Width, desc.Height, sm.RowPitch); + int compression = user_image ? Z_BEST_COMPRESSION : theApp.GetConfig("png_compression_level", Z_BEST_SPEED); + bool success = GSPng::Save(format, fn, static_cast(sm.pData), desc.Width, desc.Height, sm.RowPitch, compression); m_ctx->Unmap(res, 0); diff --git a/plugins/GSdx/GSTexture11.h b/plugins/GSdx/GSTexture11.h index 89b84e1dc0..8b9640e069 100644 --- a/plugins/GSdx/GSTexture11.h +++ b/plugins/GSdx/GSTexture11.h @@ -40,7 +40,7 @@ public: bool Update(const GSVector4i& r, const void* data, int pitch); bool Map(GSMap& m, const GSVector4i* r); void Unmap(); - bool Save(const string& fn, bool dds = false); + bool Save(const string& fn, bool user_image = false, bool dds = false); operator ID3D11Texture2D*(); operator ID3D11ShaderResourceView*(); diff --git a/plugins/GSdx/GSTexture9.cpp b/plugins/GSdx/GSTexture9.cpp index 6c09e3c7c7..e322c2ee37 100644 --- a/plugins/GSdx/GSTexture9.cpp +++ b/plugins/GSdx/GSTexture9.cpp @@ -142,7 +142,7 @@ void GSTexture9::Unmap() } } -bool GSTexture9::Save(const string& fn, bool dds) +bool GSTexture9::Save(const string& fn, bool user_image, bool dds) { bool rb_swapped = true; CComPtr surface; @@ -201,7 +201,8 @@ bool GSTexture9::Save(const string& fn, bool dds) return false; } - bool success = GSPng::Save(format, fn, static_cast(slr.pBits), desc.Width, desc.Height, slr.Pitch, rb_swapped); + int compression = user_image ? Z_BEST_COMPRESSION : theApp.GetConfig("png_compression_level", Z_BEST_SPEED); + bool success = GSPng::Save(format, fn, static_cast(slr.pBits), desc.Width, desc.Height, slr.Pitch, compression, rb_swapped); surface->UnlockRect(); return success; diff --git a/plugins/GSdx/GSTexture9.h b/plugins/GSdx/GSTexture9.h index dcf67cdf96..347734fd50 100644 --- a/plugins/GSdx/GSTexture9.h +++ b/plugins/GSdx/GSTexture9.h @@ -38,7 +38,7 @@ public: bool Update(const GSVector4i& r, const void* data, int pitch); bool Map(GSMap& m, const GSVector4i* r); void Unmap(); - bool Save(const string& fn, bool dds = false); + bool Save(const string& fn, bool user_image = false, bool dds = false); operator IDirect3DSurface9*(); operator IDirect3DTexture9*(); diff --git a/plugins/GSdx/GSTextureNull.h b/plugins/GSdx/GSTextureNull.h index 0a18d53fb4..5d443b24e7 100644 --- a/plugins/GSdx/GSTextureNull.h +++ b/plugins/GSdx/GSTextureNull.h @@ -37,5 +37,5 @@ public: bool Update(const GSVector4i& r, const void* data, int pitch) {return true;} bool Map(GSMap& m, const GSVector4i* r) {return false;} void Unmap() {} - bool Save(const string& fn, bool dds = false) {return false;} + bool Save(const string& fn, bool user_image = false, bool dds = false) { return false; } }; diff --git a/plugins/GSdx/GSTextureOGL.cpp b/plugins/GSdx/GSTextureOGL.cpp index 77c6302247..a3db8b9a30 100644 --- a/plugins/GSdx/GSTextureOGL.cpp +++ b/plugins/GSdx/GSTextureOGL.cpp @@ -421,7 +421,7 @@ void GSTextureOGL::Unmap() { } -bool GSTextureOGL::Save(const string& fn, bool dds) +bool GSTextureOGL::Save(const string& fn, bool user_image, bool dds) { // Collect the texture data uint32 pitch = 4 * m_size.x; @@ -470,7 +470,8 @@ bool GSTextureOGL::Save(const string& fn, bool dds) glBindFramebuffer(GL_READ_FRAMEBUFFER, 0); } - return GSPng::Save(fmt, fn, image.get(), m_size.x, m_size.y, pitch); + int compression = user_image ? Z_BEST_COMPRESSION : theApp.GetConfig("png_compression_level", Z_BEST_SPEED); + return GSPng::Save(fmt, fn, image.get(), m_size.x, m_size.y, pitch, compression); } uint32 GSTextureOGL::GetMemUsage() diff --git a/plugins/GSdx/GSTextureOGL.h b/plugins/GSdx/GSTextureOGL.h index 40e0af88c5..c94909e8e4 100644 --- a/plugins/GSdx/GSTextureOGL.h +++ b/plugins/GSdx/GSTextureOGL.h @@ -63,7 +63,7 @@ class GSTextureOGL : public GSTexture bool Update(const GSVector4i& r, const void* data, int pitch); bool Map(GSMap& m, const GSVector4i* r = NULL); void Unmap(); - bool Save(const string& fn, bool dds = false); + bool Save(const string& fn, bool user_image = false, bool dds = false); bool IsBackbuffer() { return (m_type == GSTexture::Backbuffer); } bool IsDss() { return (m_type == GSTexture::DepthStencil); } diff --git a/plugins/GSdx/GSTextureSW.cpp b/plugins/GSdx/GSTextureSW.cpp index b2ba42e3d4..0baba7282b 100644 --- a/plugins/GSdx/GSTextureSW.cpp +++ b/plugins/GSdx/GSTextureSW.cpp @@ -85,7 +85,7 @@ void GSTextureSW::Unmap() m_mapped.clear(); } -bool GSTextureSW::Save(const string& fn, bool dds) +bool GSTextureSW::Save(const string& fn, bool user_image, bool dds) { if(dds) return false; // not implemented @@ -94,5 +94,6 @@ bool GSTextureSW::Save(const string& fn, bool dds) #else GSPng::Format fmt = GSPng::RGB_PNG; #endif - return GSPng::Save(fmt, fn, static_cast(m_data), m_size.x, m_size.y, m_pitch); + int compression = user_image ? Z_BEST_COMPRESSION : theApp.GetConfig("png_compression_level", Z_BEST_SPEED); + return GSPng::Save(fmt, fn, static_cast(m_data), m_size.x, m_size.y, m_pitch, compression); } diff --git a/plugins/GSdx/GSTextureSW.h b/plugins/GSdx/GSTextureSW.h index 8cba020ff5..52154ee297 100644 --- a/plugins/GSdx/GSTextureSW.h +++ b/plugins/GSdx/GSTextureSW.h @@ -38,5 +38,5 @@ public: bool Update(const GSVector4i& r, const void* data, int pitch); bool Map(GSMap& m, const GSVector4i* r); void Unmap(); - bool Save(const string& fn, bool dds = false); + bool Save(const string& fn, bool user_image = false, bool dds = false); }; diff --git a/plugins/GSdx/stdafx.h b/plugins/GSdx/stdafx.h index 99d6869919..7aac17529d 100644 --- a/plugins/GSdx/stdafx.h +++ b/plugins/GSdx/stdafx.h @@ -109,6 +109,8 @@ using namespace std; #include +#include + #if _MSC_VER >= 1800 || !defined(_WIN32) #include #include