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 1f400bd921..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") @@ -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/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/plugins/GSdx/GSCapture.cpp b/plugins/GSdx/GSCapture.cpp index 809cfd7f42..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, (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_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 f4a87b9027..62b7e7d4b5 100644 --- a/plugins/GSdx/GSPng.cpp +++ b/plugins/GSdx/GSPng.cpp @@ -20,143 +20,117 @@ #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, 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, int compression, + bool rb_swapped = false, bool first_image = false) { -#ifdef ENABLE_OGL_PNG - std::string root = file; - root.replace(file.length()-4, 4, ""); + int channel_bit_depth = pixel[fmt].channel_bit_depth; + int bytes_per_pixel_in = pixel[fmt].bytes_per_pixel_in; - uint8* data = (uint8*)image; + 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; - 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; + FILE *fp = fopen(file.c_str(), "wb"); + if (fp == nullptr) + return false; - 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; + png_structp png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, nullptr, nullptr, nullptr); + png_infop info_ptr = nullptr; - 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; + bool success = false; + try { + if (png_ptr == nullptr) + throw GSDXRecoverableError(); - 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)); + info_ptr = png_create_info_struct(png_ptr); + if (info_ptr == nullptr) + throw GSDXRecoverableError(); - png::gray_pixel_16 msb(depth >> 16); - png::gray_pixel_16 lsb((depth >> 16) ? 0xFFFF : depth & 0xFFFF); + if (setjmp(png_jmpbuf(png_ptr))) + throw GSDXRecoverableError(); - img_msb[y][x] = msb; - img_lsb[y][x] = lsb; - } - } - img_msb.write(root + "_msb.png"); - img_lsb.write(root + "_lsb.png"); - } - break; + if (compression < 0 || compression > Z_BEST_COMPRESSION) + compression = Z_BEST_SPEED; - 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; + png_init_io(png_ptr, fp); + 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); - 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; + if (channel_bit_depth > 8) + png_set_swap(png_ptr); + if (rb_swapped && type != PNG_COLOR_TYPE_GRAY) + png_set_bgr(png_ptr); - 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; + 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); - 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); + success = true; + } catch (GSDXRecoverableError&) { + fprintf(stderr, "Failed to write image %s\n", file.c_str()); } -#endif + + if (png_ptr) + png_destroy_write_struct(&png_ptr, info_ptr ? &info_ptr : nullptr); + fclose(fp); + + return success; } - Transaction::Transaction(GSPng::Format fmt, const string& file, char* image, int w, int h, int pitch) - : m_fmt(fmt), m_file(file), m_w(w), m_h(h), m_pitch(pitch) + 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, ""); + + ASSERT(fmt >= Format::START && fmt < Format::COUNT); + + 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, compression, rb_swapped, true)) + return false; + + // Second image + if (pixel[fmt].extension[1] == nullptr) + return true; + + filename = root + pixel[fmt].extension[1]; + 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, 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 = (char*)_aligned_malloc(pitch*h, 32); + m_image = (uint8*)_aligned_malloc(pitch*h, 32); if (m_image) memcpy(m_image, image, pitch*h); } @@ -169,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 b328e89502..6dd623fee3 100644 --- a/plugins/GSdx/GSPng.h +++ b/plugins/GSdx/GSPng.h @@ -20,21 +20,19 @@ #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, - DEPTH_PNG, R8I_PNG, R16I_PNG, R32I_PNG, + COUNT }; class Transaction @@ -42,16 +40,17 @@ 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; + int m_compression; - 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, int compression); ~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, 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 45a17b205c..2e4cbf557b 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) @@ -92,62 +93,98 @@ void GSTexture11::Unmap() } } -bool GSTexture11::Save(const string& fn, bool dds) +bool GSTexture11::Save(const string& fn, bool user_image, 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; + } + + 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); + + return success; } GSTexture11::operator ID3D11Texture2D*() 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 ecf24195c9..e322c2ee37 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) { @@ -141,58 +142,70 @@ 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; - 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; + + 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; } GSTexture9::operator IDirect3DSurface9*() 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 c1fefd4b42..a3db8b9a30 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) +bool GSTextureOGL::Save(const string& fn, bool user_image, 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); - bool status = true; + std::unique_ptr image(new uint8[buf_size]); #ifdef ENABLE_OGL_DEBUG GSPng::Format fmt = GSPng::RGB_A_PNG; #else @@ -539,58 +434,44 @@ 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; + fmt = GSPng::RGB_A_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; - - // Not supported in Save function - status = false; - } else { glBindFramebuffer(GL_READ_FRAMEBUFFER, m_fbo_read); 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; } 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); - // Not supported in Save function - status = false; + glReadPixels(0, 0, m_size.x, m_size.y, GL_RED, GL_UNSIGNED_BYTE, image.get()); } glBindFramebuffer(GL_READ_FRAMEBUFFER, 0); } -#ifdef ENABLE_OGL_PNG - GSPng::Save(fmt, fn, image, m_size.x, m_size.y, pitch); -#else - if (status) Save(fn, image, pitch); -#endif - free(image); - - return status; + 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 1050907273..c94909e8e4 100644 --- a/plugins/GSdx/GSTextureOGL.h +++ b/plugins/GSdx/GSTextureOGL.h @@ -63,8 +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); - void Save(const string& fn, const void* image, uint32 pitch); + 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 cdd75c27ad..0baba7282b 100644 --- a/plugins/GSdx/GSTextureSW.cpp +++ b/plugins/GSdx/GSTextureSW.cpp @@ -85,106 +85,15 @@ 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) +bool GSTextureSW::Save(const string& fn, bool user_image, 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 - GSPng::Save(fmt, fn, (char*)m_data, m_size.x, m_size.y, m_pitch); - return true; - -#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 + 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/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..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 -// 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 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 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 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); } } diff --git a/travis.sh b/travis.sh index 4d326b2436..c58b53989f 100755 --- a/travis.sh +++ b/travis.sh @@ -51,10 +51,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() { @@ -94,7 +90,6 @@ linux_64_before_install() { libaio-dev \ libasound2-dev \ libgtk2.0-dev \ - libpng++-dev \ libpng12-dev \ libsdl2-dev \ libsoundtouch-dev \