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"))