mirror of https://github.com/PCSX2/pcsx2.git
Merge pull request #1197 from turtleli/gsdx-png
Gsdx: PNG screenshots (Windows), use libpng directly
This commit is contained in:
commit
3e278a2e75
|
@ -74,7 +74,6 @@ if(EGL_API)
|
||||||
endif()
|
endif()
|
||||||
check_lib(PORTAUDIO portaudio portaudio.h pa_linux_alsa.h)
|
check_lib(PORTAUDIO portaudio portaudio.h pa_linux_alsa.h)
|
||||||
check_lib(SOUNDTOUCH SoundTouch soundtouch/SoundTouch.h)
|
check_lib(SOUNDTOUCH SoundTouch soundtouch/SoundTouch.h)
|
||||||
check_lib(PNGPP FALSE png++/png.hpp)
|
|
||||||
|
|
||||||
if(SDL2_API)
|
if(SDL2_API)
|
||||||
check_lib(SDL2 SDL2 SDL.h PATH_SUFFIXES SDL2)
|
check_lib(SDL2 SDL2 SDL.h PATH_SUFFIXES SDL2)
|
||||||
|
|
|
@ -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_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_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_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_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_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")
|
set(msg_dep_zerospu2 "check these libraries -> soundtouch (>=1.5), alsa")
|
||||||
|
@ -148,9 +148,11 @@ endif()
|
||||||
# GSdx
|
# GSdx
|
||||||
#---------------------------------------
|
#---------------------------------------
|
||||||
# requires: -OpenGL
|
# 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)
|
set(GSdx TRUE)
|
||||||
elseif(NOT EXISTS "${CMAKE_SOURCE_DIR}/plugins/GSdx")
|
elseif(NOT EXISTS "${CMAKE_SOURCE_DIR}/plugins/GSdx")
|
||||||
set(GSdx FALSE)
|
set(GSdx FALSE)
|
||||||
|
|
|
@ -12,7 +12,6 @@ Build-Depends: cmake (>= 2.8.5),
|
||||||
libglu1-mesa-dev,
|
libglu1-mesa-dev,
|
||||||
libgtk2.0-dev (>= 2.16),
|
libgtk2.0-dev (>= 2.16),
|
||||||
libpng12-dev,
|
libpng12-dev,
|
||||||
libpng++-dev,
|
|
||||||
libpulse-dev,
|
libpulse-dev,
|
||||||
libsdl2-dev,
|
libsdl2-dev,
|
||||||
libsoundtouch-dev,
|
libsoundtouch-dev,
|
||||||
|
|
|
@ -31,10 +31,6 @@ if(LIBLZMA_FOUND)
|
||||||
set(GSdxFinalFlags ${GSdxFinalFlags} -DLZMA_SUPPORTED)
|
set(GSdxFinalFlags ${GSdxFinalFlags} -DLZMA_SUPPORTED)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if(PNGPP_FOUND)
|
|
||||||
set(GSdxFinalFlags ${GSdxFinalFlags} -DPNGPP_SUPPORTED)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
#Clang doesn't support a few common flags that GCC does.
|
#Clang doesn't support a few common flags that GCC does.
|
||||||
if(NOT USE_CLANG)
|
if(NOT USE_CLANG)
|
||||||
set(GSdxFinalFlags ${GSdxFinalFlags} -fabi-version=6)
|
set(GSdxFinalFlags ${GSdxFinalFlags} -fabi-version=6)
|
||||||
|
|
|
@ -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, (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<GSPng::Transaction>(new GSPng::Transaction(GSPng::RGB_PNG, out_file, (char*)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++;
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -20,143 +20,117 @@
|
||||||
|
|
||||||
#include "stdafx.h"
|
#include "stdafx.h"
|
||||||
#include "GSPng.h"
|
#include "GSPng.h"
|
||||||
|
#include <zlib.h>
|
||||||
|
#include <png.h>
|
||||||
|
|
||||||
|
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 {
|
namespace GSPng {
|
||||||
|
|
||||||
// FIXME gray_pixel_16 doesn't work. Integer image and depth image are all black
|
bool SaveFile(const string& file, Format fmt, uint8* image, uint8* row,
|
||||||
// Maybe I can't open them correctly
|
int width, int height, int pitch, int compression,
|
||||||
// A better solution must be found!
|
bool rb_swapped = false, bool first_image = false)
|
||||||
void Save(GSPng::Format fmt, const string& file, char* image, int w, int h, int pitch)
|
|
||||||
{
|
{
|
||||||
#ifdef ENABLE_OGL_PNG
|
int channel_bit_depth = pixel[fmt].channel_bit_depth;
|
||||||
std::string root = file;
|
int bytes_per_pixel_in = pixel[fmt].bytes_per_pixel_in;
|
||||||
root.replace(file.length()-4, 4, "");
|
|
||||||
|
|
||||||
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) {
|
FILE *fp = fopen(file.c_str(), "wb");
|
||||||
case R8I_PNG:
|
if (fp == nullptr)
|
||||||
{
|
return false;
|
||||||
png::image<png::gray_pixel> 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;
|
|
||||||
|
|
||||||
case R16I_PNG:
|
png_structp png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, nullptr, nullptr, nullptr);
|
||||||
{
|
png_infop info_ptr = nullptr;
|
||||||
png::image<png::gray_pixel_16> 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;
|
|
||||||
|
|
||||||
case R32I_PNG:
|
bool success = false;
|
||||||
{
|
try {
|
||||||
png::image<png::gray_pixel_16> img_msb(w, h);
|
if (png_ptr == nullptr)
|
||||||
png::image<png::gray_pixel_16> img_lsb(w, h);
|
throw GSDXRecoverableError();
|
||||||
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;
|
|
||||||
|
|
||||||
case DEPTH_PNG:
|
info_ptr = png_create_info_struct(png_ptr);
|
||||||
{
|
if (info_ptr == nullptr)
|
||||||
png::image<png::gray_pixel_16> img_msb(w, h);
|
throw GSDXRecoverableError();
|
||||||
png::image<png::gray_pixel_16> 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);
|
if (setjmp(png_jmpbuf(png_ptr)))
|
||||||
png::gray_pixel_16 lsb((depth >> 16) ? 0xFFFF : depth & 0xFFFF);
|
throw GSDXRecoverableError();
|
||||||
|
|
||||||
img_msb[y][x] = msb;
|
if (compression < 0 || compression > Z_BEST_COMPRESSION)
|
||||||
img_lsb[y][x] = lsb;
|
compression = Z_BEST_SPEED;
|
||||||
}
|
|
||||||
}
|
|
||||||
img_msb.write(root + "_msb.png");
|
|
||||||
img_lsb.write(root + "_lsb.png");
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case ALPHA_PNG:
|
png_init_io(png_ptr, fp);
|
||||||
{
|
png_set_compression_level(png_ptr, compression);
|
||||||
png::image<png::gray_pixel> img_alpha(w, h);
|
png_set_IHDR(png_ptr, info_ptr, width, height, channel_bit_depth, type,
|
||||||
for(int y = 0; y < h; y++, data += pitch) {
|
PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);
|
||||||
for (int x = 0; x < w; x++) {
|
png_write_info(png_ptr, info_ptr);
|
||||||
img_alpha[y][x] = png::gray_pixel(data[4*x+3]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
img_alpha.write(root + "_alpha.png");
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case RGB_PNG:
|
if (channel_bit_depth > 8)
|
||||||
{
|
png_set_swap(png_ptr);
|
||||||
png::image<png::rgb_pixel> img_opaque(w, h);
|
if (rb_swapped && type != PNG_COLOR_TYPE_GRAY)
|
||||||
for(int y = 0; y < h; y++, data += pitch) {
|
png_set_bgr(png_ptr);
|
||||||
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:
|
for (int y = 0; y < height; ++y, image += pitch) {
|
||||||
{
|
for (int x = 0; x < width; ++x)
|
||||||
png::image<png::rgba_pixel> img(w, h);
|
for (int i = 0; i < bytes_per_pixel_out; ++i)
|
||||||
for(int y = 0; y < h; y++, data += pitch) {
|
row[bytes_per_pixel_out * x + i] = image[bytes_per_pixel_in * x + i + offset];
|
||||||
for (int x = 0; x < w; x++) {
|
png_write_row(png_ptr, row);
|
||||||
img[y][x] = png::rgba_pixel(data[4*x+0], data[4*x+1], data[4*x+2], data[4*x+3]);
|
}
|
||||||
}
|
png_write_end(png_ptr, nullptr);
|
||||||
}
|
|
||||||
img.write(root + "_full.png");
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case RGB_A_PNG:
|
success = true;
|
||||||
{
|
} catch (GSDXRecoverableError&) {
|
||||||
png::image<png::rgb_pixel> img_opaque(w, h);
|
fprintf(stderr, "Failed to write image %s\n", file.c_str());
|
||||||
png::image<png::gray_pixel> 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);
|
|
||||||
}
|
}
|
||||||
#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)
|
bool Save(GSPng::Format fmt, const string& file, uint8* image, int w, int h, int pitch, int compression, bool rb_swapped)
|
||||||
: m_fmt(fmt), m_file(file), m_w(w), m_h(h), m_pitch(pitch)
|
{
|
||||||
|
std::string root = file;
|
||||||
|
root.replace(file.length() - 4, 4, "");
|
||||||
|
|
||||||
|
ASSERT(fmt >= Format::START && fmt < Format::COUNT);
|
||||||
|
|
||||||
|
std::unique_ptr<uint8[]> 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
|
// 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)
|
if (m_image)
|
||||||
memcpy(m_image, image, pitch*h);
|
memcpy(m_image, image, pitch*h);
|
||||||
}
|
}
|
||||||
|
@ -169,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);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,21 +20,19 @@
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#ifdef ENABLE_OGL_PNG
|
|
||||||
#include "png++/png.hpp"
|
|
||||||
#endif
|
|
||||||
#include "GSThread_CXX11.h"
|
#include "GSThread_CXX11.h"
|
||||||
|
|
||||||
namespace GSPng {
|
namespace GSPng {
|
||||||
enum Format {
|
enum Format {
|
||||||
RGBA_PNG,
|
START = 0,
|
||||||
|
RGBA_PNG = 0,
|
||||||
RGB_PNG,
|
RGB_PNG,
|
||||||
RGB_A_PNG,
|
RGB_A_PNG,
|
||||||
ALPHA_PNG,
|
ALPHA_PNG,
|
||||||
DEPTH_PNG,
|
|
||||||
R8I_PNG,
|
R8I_PNG,
|
||||||
R16I_PNG,
|
R16I_PNG,
|
||||||
R32I_PNG,
|
R32I_PNG,
|
||||||
|
COUNT
|
||||||
};
|
};
|
||||||
|
|
||||||
class Transaction
|
class Transaction
|
||||||
|
@ -42,16 +40,17 @@ namespace GSPng {
|
||||||
public:
|
public:
|
||||||
Format m_fmt;
|
Format m_fmt;
|
||||||
const std::string m_file;
|
const std::string m_file;
|
||||||
char* m_image;
|
uint8* m_image;
|
||||||
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, 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();
|
~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<shared_ptr<Transaction>, 16 >
|
class Worker : public GSJobQueue<shared_ptr<Transaction>, 16 >
|
||||||
{
|
{
|
||||||
|
|
|
@ -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();
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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; }
|
||||||
|
|
||||||
|
|
|
@ -21,6 +21,7 @@
|
||||||
|
|
||||||
#include "stdafx.h"
|
#include "stdafx.h"
|
||||||
#include "GSTexture11.h"
|
#include "GSTexture11.h"
|
||||||
|
#include "GSPng.h"
|
||||||
|
|
||||||
GSTexture11::GSTexture11(ID3D11Texture2D* texture)
|
GSTexture11::GSTexture11(ID3D11Texture2D* texture)
|
||||||
: m_texture(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<ID3D11Resource> res;
|
CComPtr<ID3D11Texture2D> 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));
|
if (m_desc.BindFlags & D3D11_BIND_DEPTH_STENCIL)
|
||||||
|
{
|
||||||
m_texture->GetDesc(&desc);
|
CComPtr<ID3D11Texture2D> dst;
|
||||||
|
|
||||||
desc.Usage = D3D11_USAGE_STAGING;
|
|
||||||
desc.BindFlags = 0;
|
|
||||||
desc.CPUAccessFlags = D3D11_CPU_ACCESS_READ;
|
|
||||||
|
|
||||||
CComPtr<ID3D11Texture2D> src, dst;
|
|
||||||
|
|
||||||
hr = m_dev->CreateTexture2D(&desc, NULL, &src);
|
|
||||||
|
|
||||||
m_ctx->CopyResource(src, m_texture);
|
|
||||||
|
|
||||||
desc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
|
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;
|
D3D11_MAPPED_SUBRESOURCE sm, dm;
|
||||||
|
|
||||||
hr = m_ctx->Map(src, 0, D3D11_MAP_READ, 0, &sm);
|
hr = m_ctx->Map(res, 0, D3D11_MAP_READ, 0, &sm);
|
||||||
hr = m_ctx->Map(dst, 0, D3D11_MAP_WRITE, 0, &dm);
|
if (FAILED(hr))
|
||||||
|
|
||||||
uint8* s = (uint8*)sm.pData;
|
|
||||||
uint8* d = (uint8*)dm.pData;
|
|
||||||
|
|
||||||
for(uint32 y = 0; y < desc.Height; y++, s += sm.RowPitch, d += dm.RowPitch)
|
|
||||||
{
|
{
|
||||||
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<uint8*>(sm.pData);
|
||||||
|
uint8* d = static_cast<uint8*>(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<uint32*>(d)[x] = static_cast<uint32>(ldexpf(reinterpret_cast<float*>(s)[x*2], 32));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
m_ctx->Unmap(src, 0);
|
m_ctx->Unmap(res, 0);
|
||||||
m_ctx->Unmap(dst, 0);
|
m_ctx->Unmap(dst, 0);
|
||||||
|
|
||||||
res = dst;
|
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<uint8*>(sm.pData), desc.Width, desc.Height, sm.RowPitch, compression);
|
||||||
|
|
||||||
|
m_ctx->Unmap(res, 0);
|
||||||
|
|
||||||
|
return success;
|
||||||
}
|
}
|
||||||
|
|
||||||
GSTexture11::operator ID3D11Texture2D*()
|
GSTexture11::operator ID3D11Texture2D*()
|
||||||
|
|
|
@ -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*();
|
||||||
|
|
|
@ -21,6 +21,7 @@
|
||||||
|
|
||||||
#include "stdafx.h"
|
#include "stdafx.h"
|
||||||
#include "GSTexture9.h"
|
#include "GSTexture9.h"
|
||||||
|
#include "GSPng.h"
|
||||||
|
|
||||||
GSTexture9::GSTexture9(IDirect3DSurface9* surface)
|
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<IDirect3DSurface9> surface;
|
CComPtr<IDirect3DSurface9> 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;
|
if (desc.Format == D3DFMT_A8 || desc.Pool == D3DPOOL_MANAGED || desc.Usage == D3DUSAGE_DEPTHSTENCIL)
|
||||||
|
{
|
||||||
m_surface->GetDesc(&desc);
|
surface = m_surface;
|
||||||
|
rb_swapped = false;
|
||||||
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();
|
|
||||||
}
|
}
|
||||||
else
|
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<IDirect3DTexture9> 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<uint8*>(slr.pBits), desc.Width, desc.Height, slr.Pitch, compression, rb_swapped);
|
||||||
|
|
||||||
|
surface->UnlockRect();
|
||||||
|
return success;
|
||||||
}
|
}
|
||||||
|
|
||||||
GSTexture9::operator IDirect3DSurface9*()
|
GSTexture9::operator IDirect3DSurface9*()
|
||||||
|
|
|
@ -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*();
|
||||||
|
|
|
@ -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; }
|
||||||
};
|
};
|
||||||
|
|
|
@ -421,117 +421,12 @@ void GSTextureOGL::Unmap()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifndef _WIN32
|
bool GSTextureOGL::Save(const string& fn, bool user_image, bool dds)
|
||||||
|
|
||||||
#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
|
// Collect the texture data
|
||||||
uint32 pitch = 4 * m_size.x;
|
uint32 pitch = 4 * m_size.x;
|
||||||
uint32 buf_size = pitch * m_size.y * 2;// Note *2 for security (depth/stencil)
|
uint32 buf_size = pitch * m_size.y * 2;// Note *2 for security (depth/stencil)
|
||||||
char* image = (char*)malloc(buf_size);
|
std::unique_ptr<uint8[]> image(new uint8[buf_size]);
|
||||||
bool status = true;
|
|
||||||
#ifdef ENABLE_OGL_DEBUG
|
#ifdef ENABLE_OGL_DEBUG
|
||||||
GSPng::Format fmt = GSPng::RGB_A_PNG;
|
GSPng::Format fmt = GSPng::RGB_A_PNG;
|
||||||
#else
|
#else
|
||||||
|
@ -539,58 +434,44 @@ bool GSTextureOGL::Save(const string& fn, bool dds)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if (IsBackbuffer()) {
|
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()) {
|
} else if(IsDss()) {
|
||||||
glBindFramebuffer(GL_READ_FRAMEBUFFER, m_fbo_read);
|
glBindFramebuffer(GL_READ_FRAMEBUFFER, m_fbo_read);
|
||||||
|
|
||||||
glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, m_texture_id, 0);
|
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);
|
glBindFramebuffer(GL_READ_FRAMEBUFFER, 0);
|
||||||
|
|
||||||
fmt = GSPng::DEPTH_PNG;
|
fmt = GSPng::RGB_A_PNG;
|
||||||
} else if(m_format == GL_R32I) {
|
} 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;
|
fmt = GSPng::R32I_PNG;
|
||||||
|
|
||||||
// Not supported in Save function
|
|
||||||
status = false;
|
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
glBindFramebuffer(GL_READ_FRAMEBUFFER, m_fbo_read);
|
glBindFramebuffer(GL_READ_FRAMEBUFFER, m_fbo_read);
|
||||||
|
|
||||||
glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, m_texture_id, 0);
|
glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, m_texture_id, 0);
|
||||||
|
|
||||||
if (m_format == GL_RGBA8) {
|
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)
|
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;
|
fmt = GSPng::R16I_PNG;
|
||||||
// Not supported in Save function
|
|
||||||
status = false;
|
|
||||||
}
|
}
|
||||||
else if (m_format == GL_R8)
|
else if (m_format == GL_R8)
|
||||||
{
|
{
|
||||||
fmt = GSPng::R8I_PNG;
|
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
glBindFramebuffer(GL_READ_FRAMEBUFFER, 0);
|
glBindFramebuffer(GL_READ_FRAMEBUFFER, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef ENABLE_OGL_PNG
|
int compression = user_image ? Z_BEST_COMPRESSION : theApp.GetConfig("png_compression_level", Z_BEST_SPEED);
|
||||||
GSPng::Save(fmt, fn, image, m_size.x, m_size.y, pitch);
|
return GSPng::Save(fmt, fn, image.get(), m_size.x, m_size.y, pitch, compression);
|
||||||
#else
|
|
||||||
if (status) Save(fn, image, pitch);
|
|
||||||
#endif
|
|
||||||
free(image);
|
|
||||||
|
|
||||||
return status;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32 GSTextureOGL::GetMemUsage()
|
uint32 GSTextureOGL::GetMemUsage()
|
||||||
|
|
|
@ -63,8 +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);
|
||||||
void Save(const string& fn, const void* image, uint32 pitch);
|
|
||||||
|
|
||||||
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); }
|
||||||
|
|
|
@ -85,106 +85,15 @@ void GSTextureSW::Unmap()
|
||||||
m_mapped.clear();
|
m_mapped.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifndef _WIN32
|
bool GSTextureSW::Save(const string& fn, bool user_image, bool dds)
|
||||||
|
|
||||||
#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
|
if(dds) return false; // not implemented
|
||||||
|
|
||||||
#ifdef ENABLE_OGL_PNG
|
|
||||||
|
|
||||||
#ifdef ENABLE_OGL_DEBUG
|
#ifdef ENABLE_OGL_DEBUG
|
||||||
GSPng::Format fmt = GSPng::RGB_A_PNG;
|
GSPng::Format fmt = GSPng::RGB_A_PNG;
|
||||||
#else
|
#else
|
||||||
GSPng::Format fmt = GSPng::RGB_PNG;
|
GSPng::Format fmt = GSPng::RGB_PNG;
|
||||||
#endif
|
#endif
|
||||||
GSPng::Save(fmt, fn, (char*)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 true;
|
return GSPng::Save(fmt, fn, static_cast<uint8*>(m_data), m_size.x, m_size.y, m_pitch, compression);
|
||||||
|
|
||||||
#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
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
};
|
};
|
||||||
|
|
|
@ -692,6 +692,7 @@
|
||||||
<ClCompile Include="GSFunctionMap.cpp" />
|
<ClCompile Include="GSFunctionMap.cpp" />
|
||||||
<ClCompile Include="GSLocalMemory.cpp" />
|
<ClCompile Include="GSLocalMemory.cpp" />
|
||||||
<ClCompile Include="GSPerfMon.cpp" />
|
<ClCompile Include="GSPerfMon.cpp" />
|
||||||
|
<ClCompile Include="GSPng.cpp" />
|
||||||
<ClCompile Include="GSRasterizer.cpp">
|
<ClCompile Include="GSRasterizer.cpp">
|
||||||
<AssemblerOutput Condition="'$(Configuration)|$(Platform)'=='Release AVX|x64'">AssemblyAndSourceCode</AssemblerOutput>
|
<AssemblerOutput Condition="'$(Configuration)|$(Platform)'=='Release AVX|x64'">AssemblyAndSourceCode</AssemblerOutput>
|
||||||
<AssemblerOutput Condition="'$(Configuration)|$(Platform)'=='Release AVX2|x64'">AssemblyAndSourceCode</AssemblerOutput>
|
<AssemblerOutput Condition="'$(Configuration)|$(Platform)'=='Release AVX2|x64'">AssemblyAndSourceCode</AssemblerOutput>
|
||||||
|
@ -1980,6 +1981,7 @@
|
||||||
<ClInclude Include="GSFunctionMap.h" />
|
<ClInclude Include="GSFunctionMap.h" />
|
||||||
<ClInclude Include="GSLocalMemory.h" />
|
<ClInclude Include="GSLocalMemory.h" />
|
||||||
<ClInclude Include="GSPerfMon.h" />
|
<ClInclude Include="GSPerfMon.h" />
|
||||||
|
<ClInclude Include="GSPng.h" />
|
||||||
<ClInclude Include="GSRasterizer.h" />
|
<ClInclude Include="GSRasterizer.h" />
|
||||||
<ClInclude Include="GSRenderer.h" />
|
<ClInclude Include="GSRenderer.h" />
|
||||||
<ClInclude Include="GSRendererCL.h" />
|
<ClInclude Include="GSRendererCL.h" />
|
||||||
|
@ -2083,6 +2085,9 @@
|
||||||
<Image Include="res\logo-ogl.bmp" />
|
<Image Include="res\logo-ogl.bmp" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\..\3rdparty\libpng\projects\vstudio\libpng\libpng.vcxproj">
|
||||||
|
<Project>{d6973076-9317-4ef2-a0b8-b7a18ac0713e}</Project>
|
||||||
|
</ProjectReference>
|
||||||
<ProjectReference Include="..\..\3rdparty\opencl\opencl.vcxproj">
|
<ProjectReference Include="..\..\3rdparty\opencl\opencl.vcxproj">
|
||||||
<Project>{d80d4a75-c385-41bd-ae62-83d2e2b595a7}</Project>
|
<Project>{d80d4a75-c385-41bd-ae62-83d2e2b595a7}</Project>
|
||||||
<ReferenceOutputAssembly>false</ReferenceOutputAssembly>
|
<ReferenceOutputAssembly>false</ReferenceOutputAssembly>
|
||||||
|
|
|
@ -354,6 +354,9 @@
|
||||||
<ClCompile Include="GSDrawingContext.cpp">
|
<ClCompile Include="GSDrawingContext.cpp">
|
||||||
<Filter>Source Files</Filter>
|
<Filter>Source Files</Filter>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
|
<ClCompile Include="GSPng.cpp">
|
||||||
|
<Filter>Source Files</Filter>
|
||||||
|
</ClCompile>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ClInclude Include="GLLoader.h">
|
<ClInclude Include="GLLoader.h">
|
||||||
|
@ -716,6 +719,9 @@
|
||||||
<ClInclude Include="GSRendererCL.h">
|
<ClInclude Include="GSRendererCL.h">
|
||||||
<Filter>Header Files</Filter>
|
<Filter>Header Files</Filter>
|
||||||
</ClInclude>
|
</ClInclude>
|
||||||
|
<ClInclude Include="GSPng.h">
|
||||||
|
<Filter>Header Files</Filter>
|
||||||
|
</ClInclude>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<None Include="res\logo10.bmp">
|
<None Include="res\logo10.bmp">
|
||||||
|
|
|
@ -52,9 +52,3 @@
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
//#define ENABLE_OPENCL
|
//#define ENABLE_OPENCL
|
||||||
#endif
|
#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
|
|
||||||
|
|
|
@ -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>
|
||||||
|
|
|
@ -14,7 +14,7 @@
|
||||||
<WarningLevel>Level4</WarningLevel>
|
<WarningLevel>Level4</WarningLevel>
|
||||||
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
|
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
|
||||||
<DisableSpecificWarnings>4456;4458;4996;4995;4324;4100;4101;4201;4556;4127;4512;%(DisableSpecificWarnings)</DisableSpecificWarnings>
|
<DisableSpecificWarnings>4456;4458;4996;4995;4324;4100;4101;4201;4556;4127;4512;%(DisableSpecificWarnings)</DisableSpecificWarnings>
|
||||||
<AdditionalIncludeDirectories>$(DXSDK_DIR)include;$(VTUNE_AMPLIFIER_XE_2015_DIR)include;$(SolutionDir)3rdparty;$(SolutionDir)3rdparty\opencl;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
<AdditionalIncludeDirectories>$(DXSDK_DIR)include;$(VTUNE_AMPLIFIER_XE_2015_DIR)include;$(SolutionDir)3rdparty;$(SolutionDir)3rdparty\libpng;$(SolutionDir)3rdparty\opencl;$(SolutionDir)3rdparty\zlib;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||||
<MultiProcessorCompilation>true</MultiProcessorCompilation>
|
<MultiProcessorCompilation>true</MultiProcessorCompilation>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
<Link>
|
<Link>
|
||||||
|
|
|
@ -293,19 +293,22 @@ namespace GSDumpGUI
|
||||||
{
|
{
|
||||||
if (lstDumps.SelectedIndex != -1)
|
if (lstDumps.SelectedIndex != -1)
|
||||||
{
|
{
|
||||||
|
String [] Extensions = new String[] { ".png", ".bmp" };
|
||||||
String DumpFileName = lstDumps.SelectedItem.ToString().Split(new char[] { '|' })[0];
|
String DumpFileName = lstDumps.SelectedItem.ToString().Split(new char[] { '|' })[0];
|
||||||
String Filename = Path.GetDirectoryName(Properties.Settings.Default.DumpDir + "\\") +
|
String Filename = Path.GetDirectoryName(Properties.Settings.Default.DumpDir + "\\") +
|
||||||
"\\" + Path.GetFileNameWithoutExtension(DumpFileName) + ".bmp";
|
"\\" + Path.GetFileNameWithoutExtension(DumpFileName);
|
||||||
if (File.Exists(Filename))
|
|
||||||
|
foreach (String Extension in Extensions)
|
||||||
{
|
{
|
||||||
pctBox.Image = Image.FromFile(Filename);
|
if (File.Exists(Filename + Extension))
|
||||||
pctBox.Cursor = Cursors.Hand;
|
{
|
||||||
}
|
pctBox.Load(Filename + Extension);
|
||||||
else
|
pctBox.Cursor = Cursors.Hand;
|
||||||
{
|
return;
|
||||||
pctBox.Image = NoImage;
|
}
|
||||||
pctBox.Cursor = Cursors.Default;
|
|
||||||
}
|
}
|
||||||
|
pctBox.Image = NoImage;
|
||||||
|
pctBox.Cursor = Cursors.Default;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -313,10 +316,7 @@ namespace GSDumpGUI
|
||||||
{
|
{
|
||||||
if (pctBox.Cursor == Cursors.Hand)
|
if (pctBox.Cursor == Cursors.Hand)
|
||||||
{
|
{
|
||||||
String DumpFileName = lstDumps.SelectedItem.ToString().Split(new char[] { '|' })[0];
|
Process.Start(pctBox.ImageLocation);
|
||||||
String Filename = Path.GetDirectoryName(Properties.Settings.Default.DumpDir + "\\") +
|
|
||||||
"\\" + Path.GetFileNameWithoutExtension(DumpFileName) + ".bmp";
|
|
||||||
Process.Start(Filename);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -51,10 +51,6 @@ linux_32_before_install() {
|
||||||
portaudio19-dev:i386 \
|
portaudio19-dev:i386 \
|
||||||
zlib1g-dev:i386 \
|
zlib1g-dev:i386 \
|
||||||
${COMPILER_PACKAGE}
|
${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() {
|
linux_32_script() {
|
||||||
|
@ -94,7 +90,6 @@ linux_64_before_install() {
|
||||||
libaio-dev \
|
libaio-dev \
|
||||||
libasound2-dev \
|
libasound2-dev \
|
||||||
libgtk2.0-dev \
|
libgtk2.0-dev \
|
||||||
libpng++-dev \
|
|
||||||
libpng12-dev \
|
libpng12-dev \
|
||||||
libsdl2-dev \
|
libsdl2-dev \
|
||||||
libsoundtouch-dev \
|
libsoundtouch-dev \
|
||||||
|
|
Loading…
Reference in New Issue