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.
This commit is contained in:
Jonathan Li 2016-02-24 21:52:17 +00:00
parent a97cbf038c
commit 6b950d0408
17 changed files with 43 additions and 31 deletions

View File

@ -488,6 +488,8 @@ bool GSCapture::BeginCapture(float fps, GSVector2i recomendedResolution, float a
m_size.x = theApp.GetConfig("capture_resx", 1280); m_size.x = theApp.GetConfig("capture_resx", 1280);
m_size.y = theApp.GetConfig("capture_resy", 1024); m_size.y = theApp.GetConfig("capture_resy", 1024);
m_compression_level = theApp.GetConfig("png_compression_level", Z_BEST_SPEED);
#ifdef __linux__ #ifdef __linux__
for(int i = 0; i < m_threads; i++) { for(int i = 0; i < m_threads; i++) {
m_workers.push_back(new GSPng::Worker()); m_workers.push_back(new GSPng::Worker());
@ -524,8 +526,8 @@ bool GSCapture::DeliverFrame(const void* bits, int pitch, bool rgba)
#elif __linux__ #elif __linux__
std::string out_file = m_out_dir + format("/frame.%010d.png", m_frame); 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); //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<GSPng::Transaction>(new GSPng::Transaction(GSPng::RGB_PNG, out_file, static_cast<const uint8*>(bits), m_size.x, m_size.y, pitch))); m_workers[m_frame%m_threads]->Push(shared_ptr<GSPng::Transaction>(new GSPng::Transaction(GSPng::RGB_PNG, out_file, static_cast<const uint8*>(bits), m_size.x, m_size.y, pitch, m_compression_level)));
m_frame++; m_frame++;

View File

@ -45,6 +45,7 @@ class GSCapture
#elif __linux__ #elif __linux__
vector<GSPng::Worker*> m_workers; vector<GSPng::Worker*> m_workers;
int m_compression_level;
#endif #endif

View File

@ -42,7 +42,7 @@ struct {
namespace GSPng { namespace GSPng {
bool SaveFile(const string& file, Format fmt, uint8* image, uint8* row, 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) bool rb_swapped = false, bool first_image = false)
{ {
int channel_bit_depth = pixel[fmt].channel_bit_depth; int channel_bit_depth = pixel[fmt].channel_bit_depth;
@ -71,8 +71,11 @@ namespace GSPng {
if (setjmp(png_jmpbuf(png_ptr))) if (setjmp(png_jmpbuf(png_ptr)))
throw GSDXRecoverableError(); throw GSDXRecoverableError();
if (compression < 0 || compression > Z_BEST_COMPRESSION)
compression = Z_BEST_SPEED;
png_init_io(png_ptr, fp); 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_set_IHDR(png_ptr, info_ptr, width, height, channel_bit_depth, type,
PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT); PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);
png_write_info(png_ptr, info_ptr); png_write_info(png_ptr, info_ptr);
@ -91,8 +94,7 @@ namespace GSPng {
png_write_end(png_ptr, nullptr); png_write_end(png_ptr, nullptr);
success = true; success = true;
} } catch (GSDXRecoverableError&) {
catch (GSDXRecoverableError&) {
fprintf(stderr, "Failed to write image %s\n", file.c_str()); fprintf(stderr, "Failed to write image %s\n", file.c_str());
} }
@ -103,7 +105,7 @@ namespace GSPng {
return success; 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; std::string root = file;
root.replace(file.length() - 4, 4, ""); root.replace(file.length() - 4, 4, "");
@ -113,7 +115,7 @@ namespace GSPng {
std::unique_ptr<uint8[]> row(new uint8[pixel[fmt].bytes_per_pixel_out * w]); std::unique_ptr<uint8[]> row(new uint8[pixel[fmt].bytes_per_pixel_out * w]);
std::string filename = root + pixel[fmt].extension[0]; 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; return false;
// Second image // Second image
@ -121,11 +123,11 @@ namespace GSPng {
return true; return true;
filename = root + pixel[fmt].extension[1]; 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) 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_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 // Note: yes it would be better to use shared pointer
m_image = (uint8*)_aligned_malloc(pitch*h, 32); m_image = (uint8*)_aligned_malloc(pitch*h, 32);
@ -141,7 +143,7 @@ namespace GSPng {
void Worker::Process(shared_ptr<Transaction>& item) void Worker::Process(shared_ptr<Transaction>& 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);
} }
} }

View File

@ -44,12 +44,13 @@ namespace GSPng {
int m_w; int m_w;
int m_h; int m_h;
int m_pitch; 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(); ~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<shared_ptr<Transaction>, 16 > class Worker : public GSJobQueue<shared_ptr<Transaction>, 16 >
{ {

View File

@ -469,7 +469,7 @@ void GSRenderer::VSync(int field)
if(GSTexture* t = m_dev->GetCurrent()) if(GSTexture* t = m_dev->GetCurrent())
{ {
t->Save(m_snapshot + ".bmp"); t->Save(m_snapshot + ".bmp", true);
} }
m_snapshot.clear(); m_snapshot.clear();

View File

@ -445,13 +445,13 @@ void GSRendererHW::Draw()
(int)context->CLAMP.MINU, (int)context->CLAMP.MAXU, (int)context->CLAMP.MINU, (int)context->CLAMP.MAXU,
(int)context->CLAMP.MINV, (int)context->CLAMP.MAXV); (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) if(tex->m_palette)
{ {
s = format("%05d_f%lld_tpx_%05x_%d.dds", s_n, frame, context->TEX0.CBP, context->TEX0.CPSM); 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);
} }
} }

View File

@ -46,7 +46,7 @@ public:
virtual bool Update(const GSVector4i& r, const void* data, int pitch) = 0; virtual bool Update(const GSVector4i& r, const void* data, int pitch) = 0;
virtual bool Map(GSMap& m, const GSVector4i* r = NULL) = 0; virtual bool Map(GSMap& m, const GSVector4i* r = NULL) = 0;
virtual void Unmap() = 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 void Invalidate() {}
virtual uint32 GetID() { return 0; } virtual uint32 GetID() { return 0; }

View File

@ -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<ID3D11Texture2D> res; CComPtr<ID3D11Texture2D> res;
D3D11_TEXTURE2D_DESC desc; D3D11_TEXTURE2D_DESC desc;
@ -179,7 +179,8 @@ bool GSTexture11::Save(const string& fn, bool dds)
return false; return false;
} }
bool success = GSPng::Save(format, fn, static_cast<uint8*>(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<uint8*>(sm.pData), desc.Width, desc.Height, sm.RowPitch, compression);
m_ctx->Unmap(res, 0); m_ctx->Unmap(res, 0);

View File

@ -40,7 +40,7 @@ public:
bool Update(const GSVector4i& r, const void* data, int pitch); bool Update(const GSVector4i& r, const void* data, int pitch);
bool Map(GSMap& m, const GSVector4i* r); bool Map(GSMap& m, const GSVector4i* r);
void Unmap(); 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 ID3D11Texture2D*();
operator ID3D11ShaderResourceView*(); operator ID3D11ShaderResourceView*();

View File

@ -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; bool rb_swapped = true;
CComPtr<IDirect3DSurface9> surface; CComPtr<IDirect3DSurface9> surface;
@ -201,7 +201,8 @@ bool GSTexture9::Save(const string& fn, bool dds)
return false; return false;
} }
bool success = GSPng::Save(format, fn, static_cast<uint8*>(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<uint8*>(slr.pBits), desc.Width, desc.Height, slr.Pitch, compression, rb_swapped);
surface->UnlockRect(); surface->UnlockRect();
return success; return success;

View File

@ -38,7 +38,7 @@ public:
bool Update(const GSVector4i& r, const void* data, int pitch); bool Update(const GSVector4i& r, const void* data, int pitch);
bool Map(GSMap& m, const GSVector4i* r); bool Map(GSMap& m, const GSVector4i* r);
void Unmap(); 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 IDirect3DSurface9*();
operator IDirect3DTexture9*(); operator IDirect3DTexture9*();

View File

@ -37,5 +37,5 @@ public:
bool Update(const GSVector4i& r, const void* data, int pitch) {return true;} bool Update(const GSVector4i& r, const void* data, int pitch) {return true;}
bool Map(GSMap& m, const GSVector4i* r) {return false;} bool Map(GSMap& m, const GSVector4i* r) {return false;}
void Unmap() {} 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; }
}; };

View File

@ -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 // Collect the texture data
uint32 pitch = 4 * m_size.x; uint32 pitch = 4 * m_size.x;
@ -470,7 +470,8 @@ bool GSTextureOGL::Save(const string& fn, bool dds)
glBindFramebuffer(GL_READ_FRAMEBUFFER, 0); 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() uint32 GSTextureOGL::GetMemUsage()

View File

@ -63,7 +63,7 @@ class GSTextureOGL : public GSTexture
bool Update(const GSVector4i& r, const void* data, int pitch); bool Update(const GSVector4i& r, const void* data, int pitch);
bool Map(GSMap& m, const GSVector4i* r = NULL); bool Map(GSMap& m, const GSVector4i* r = NULL);
void Unmap(); 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 IsBackbuffer() { return (m_type == GSTexture::Backbuffer); }
bool IsDss() { return (m_type == GSTexture::DepthStencil); } bool IsDss() { return (m_type == GSTexture::DepthStencil); }

View File

@ -85,7 +85,7 @@ void GSTextureSW::Unmap()
m_mapped.clear(); 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 if(dds) return false; // not implemented
@ -94,5 +94,6 @@ bool GSTextureSW::Save(const string& fn, bool dds)
#else #else
GSPng::Format fmt = GSPng::RGB_PNG; GSPng::Format fmt = GSPng::RGB_PNG;
#endif #endif
return GSPng::Save(fmt, fn, static_cast<uint8*>(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<uint8*>(m_data), m_size.x, m_size.y, m_pitch, compression);
} }

View File

@ -38,5 +38,5 @@ public:
bool Update(const GSVector4i& r, const void* data, int pitch); bool Update(const GSVector4i& r, const void* data, int pitch);
bool Map(GSMap& m, const GSVector4i* r); bool Map(GSMap& m, const GSVector4i* r);
void Unmap(); void Unmap();
bool Save(const string& fn, bool dds = false); bool Save(const string& fn, bool user_image = false, bool dds = false);
}; };

View File

@ -109,6 +109,8 @@ using namespace std;
#include <memory> #include <memory>
#include <zlib.h>
#if _MSC_VER >= 1800 || !defined(_WIN32) #if _MSC_VER >= 1800 || !defined(_WIN32)
#include <unordered_map> #include <unordered_map>
#include <unordered_set> #include <unordered_set>