mirror of https://github.com/PCSX2/pcsx2.git
gsdx-png: Use libpng directly instead of via png++
v2: Increase compression to maximum v3: Add zlib to GSdx build dependencies v4: Reduce memory usage
This commit is contained in:
parent
edef3ad8c3
commit
87dcfbc645
|
@ -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)
|
||||||
|
|
|
@ -524,8 +524,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_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_frame++;
|
m_frame++;
|
||||||
|
|
||||||
|
|
|
@ -20,143 +20,120 @@
|
||||||
|
|
||||||
#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, 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 {
|
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,
|
||||||
// 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)
|
{
|
||||||
|
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
|
#ifdef ENABLE_OGL_PNG
|
||||||
std::string root = file;
|
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) {
|
std::unique_ptr<uint8[]> row(new uint8[pixel[fmt].bytes_per_pixel_out * w]);
|
||||||
case R8I_PNG:
|
|
||||||
{
|
|
||||||
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:
|
std::string filename = root + pixel[fmt].extension[0];
|
||||||
{
|
if (!SaveFile(filename, fmt, image, row.get(), w, h, pitch, rb_swapped, true))
|
||||||
png::image<png::gray_pixel_16> img(w, h);
|
return false;
|
||||||
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:
|
// Second image
|
||||||
{
|
if (pixel[fmt].extension[1] == nullptr)
|
||||||
png::image<png::gray_pixel_16> img_msb(w, h);
|
return true;
|
||||||
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++) {
|
|
||||||
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:
|
filename = root + pixel[fmt].extension[1];
|
||||||
{
|
return SaveFile(filename, fmt, image, row.get(), w, h, pitch);
|
||||||
png::image<png::gray_pixel_16> img_msb(w, h);
|
#else
|
||||||
png::image<png::gray_pixel_16> img_lsb(w, h);
|
return false;
|
||||||
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<png::gray_pixel> 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<png::rgb_pixel> 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<png::rgba_pixel> 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<png::rgb_pixel> img_opaque(w, h);
|
|
||||||
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
|
#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)
|
: 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
|
// 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);
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,14 +20,12 @@
|
||||||
|
|
||||||
#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,
|
||||||
|
@ -35,6 +33,7 @@ namespace GSPng {
|
||||||
R8I_PNG,
|
R8I_PNG,
|
||||||
R16I_PNG,
|
R16I_PNG,
|
||||||
R32I_PNG,
|
R32I_PNG,
|
||||||
|
COUNT
|
||||||
};
|
};
|
||||||
|
|
||||||
class Transaction
|
class Transaction
|
||||||
|
@ -42,16 +41,16 @@ 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;
|
||||||
|
|
||||||
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();
|
~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<shared_ptr<Transaction>, 16 >
|
class Worker : public GSJobQueue<shared_ptr<Transaction>, 16 >
|
||||||
{
|
{
|
||||||
|
|
|
@ -530,7 +530,7 @@ 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;
|
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;
|
||||||
|
@ -539,18 +539,18 @@ 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::DEPTH_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;
|
||||||
|
|
||||||
|
@ -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);
|
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
|
// Not supported in Save function
|
||||||
status = false;
|
status = false;
|
||||||
|
@ -575,7 +575,7 @@ bool GSTextureOGL::Save(const string& fn, bool dds)
|
||||||
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
|
// Not supported in Save function
|
||||||
status = false;
|
status = false;
|
||||||
}
|
}
|
||||||
|
@ -584,11 +584,10 @@ bool GSTextureOGL::Save(const string& fn, bool dds)
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef ENABLE_OGL_PNG
|
#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
|
#else
|
||||||
if (status) Save(fn, image, pitch);
|
if (status) Save(fn, image.get(), pitch);
|
||||||
#endif
|
#endif
|
||||||
free(image);
|
|
||||||
|
|
||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
|
|
|
@ -130,8 +130,7 @@ bool GSTextureSW::Save(const string& fn, bool dds)
|
||||||
#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);
|
return GSPng::Save(fmt, fn, static_cast<uint8*>(m_data), m_size.x, m_size.y, m_pitch);
|
||||||
return true;
|
|
||||||
|
|
||||||
#else
|
#else
|
||||||
if(FILE* fp = fopen(fn.c_str(), "wb"))
|
if(FILE* fp = fopen(fn.c_str(), "wb"))
|
||||||
|
|
Loading…
Reference in New Issue