- added new default PNG format for doing screenshots AND the saving to disk is queued in a background thread allowing almost no delay when saving many screenshots!
This commit is contained in:
parent
369a0dc215
commit
77bad3ce07
|
@ -1197,13 +1197,15 @@ void Game::keyDown(char key) {
|
|||
string path = GameConstants::folder_path_screenshots;
|
||||
if(isdir(path.c_str()) == true) {
|
||||
Config &config= Config::getInstance();
|
||||
string fileFormat = config.getString("ScreenShotFileType","bmp");
|
||||
string fileFormat = config.getString("ScreenShotFileType","png");
|
||||
|
||||
for(int i=0; i<250; ++i){
|
||||
unsigned int queueSize = Renderer::getInstance().getSaveScreenQueueSize();
|
||||
|
||||
for(int i=0; i < 250; ++i) {
|
||||
path = GameConstants::folder_path_screenshots;
|
||||
path += "screen" + intToStr(i) + "." + fileFormat;
|
||||
path += "screen" + intToStr(i + queueSize) + "." + fileFormat;
|
||||
FILE *f= fopen(path.c_str(), "rb");
|
||||
if(f==NULL) {
|
||||
if(f == NULL) {
|
||||
Renderer::getInstance().saveScreen(path);
|
||||
break;
|
||||
}
|
||||
|
|
|
@ -152,12 +152,11 @@ Renderer::Renderer() {
|
|||
modelRenderer = NULL;
|
||||
textRenderer = NULL;
|
||||
particleRenderer = NULL;
|
||||
saveScreenShotThread = NULL;
|
||||
|
||||
lastRenderFps=MIN_FPS_NORMAL_RENDERING;
|
||||
shadowsOffDueToMinRender=false;
|
||||
|
||||
pixmapScreenShot = NULL;;
|
||||
|
||||
//resources
|
||||
for(int i=0; i < rsCount; ++i) {
|
||||
modelManager[i] = NULL;
|
||||
|
@ -189,9 +188,13 @@ Renderer::Renderer() {
|
|||
particleManager[i]= graphicsFactory->newParticleManager();
|
||||
fontManager[i]= graphicsFactory->newFontManager();
|
||||
}
|
||||
|
||||
saveScreenShotThread = new SimpleTaskThread(this,0,25);
|
||||
saveScreenShotThread->setUniqueID(__FILE__);
|
||||
saveScreenShotThread->start();
|
||||
}
|
||||
|
||||
Renderer::~Renderer(){
|
||||
Renderer::~Renderer() {
|
||||
delete modelRenderer;
|
||||
modelRenderer = NULL;
|
||||
delete textRenderer;
|
||||
|
@ -211,12 +214,50 @@ Renderer::~Renderer(){
|
|||
fontManager[i] = NULL;
|
||||
}
|
||||
|
||||
delete pixmapScreenShot;
|
||||
// Wait for the queue to become empty or timeout the thread at 7 seconds
|
||||
for(time_t elapsed = time(NULL);
|
||||
getSaveScreenQueueSize() > 0 && difftime(time(NULL),elapsed) <= 7;) {
|
||||
sleep(10);
|
||||
}
|
||||
delete saveScreenShotThread;
|
||||
saveScreenShotThread = NULL;
|
||||
|
||||
if(getSaveScreenQueueSize() > 0) {
|
||||
SystemFlags::OutputDebug(SystemFlags::debugSystem,"In [%s::%s Line %d] FORCING MEMORY CLEANUP and NOT SAVING screenshots, saveScreenQueue.size() = %d\n",__FILE__,__FUNCTION__,__LINE__,saveScreenQueue.size());
|
||||
|
||||
for(std::list<std::pair<string,Pixmap2D *> >::iterator iter = saveScreenQueue.begin();
|
||||
iter != saveScreenQueue.end(); ++iter) {
|
||||
delete iter->second;
|
||||
}
|
||||
}
|
||||
|
||||
this->menu = NULL;
|
||||
this->game = NULL;
|
||||
}
|
||||
|
||||
void Renderer::simpleTask() {
|
||||
// This code reads pixmaps from a queue and saves them to disk
|
||||
Pixmap2D *savePixMapBuffer=NULL;
|
||||
string path="";
|
||||
MutexSafeWrapper safeMutex(&saveScreenShotThreadAccessor);
|
||||
if(saveScreenQueue.size() > 0) {
|
||||
SystemFlags::OutputDebug(SystemFlags::debugSystem,"In [%s::%s Line %d] saveScreenQueue.size() = %d\n",__FILE__,__FUNCTION__,__LINE__,saveScreenQueue.size());
|
||||
|
||||
savePixMapBuffer = saveScreenQueue.front().second;
|
||||
path = saveScreenQueue.front().first;
|
||||
|
||||
saveScreenQueue.pop_front();
|
||||
}
|
||||
safeMutex.ReleaseLock();
|
||||
|
||||
if(savePixMapBuffer != NULL) {
|
||||
SystemFlags::OutputDebug(SystemFlags::debugSystem,"In [%s::%s Line %d] about to save [%s]\n",__FILE__,__FUNCTION__,__LINE__,path.c_str());
|
||||
|
||||
savePixMapBuffer->save(path);
|
||||
delete savePixMapBuffer;
|
||||
}
|
||||
}
|
||||
|
||||
Renderer &Renderer::getInstance(){
|
||||
static Renderer renderer;
|
||||
return renderer;
|
||||
|
@ -2945,21 +2986,13 @@ void Renderer::loadConfig(){
|
|||
}
|
||||
}
|
||||
|
||||
void Renderer::saveScreen(const string &path){
|
||||
|
||||
void Renderer::saveScreen(const string &path) {
|
||||
const Metrics &sm= Metrics::getInstance();
|
||||
|
||||
SystemFlags::OutputDebug(SystemFlags::debugSystem,"In [%s::%s Line: %d]\n",__FILE__,__FUNCTION__,__LINE__);
|
||||
|
||||
//Pixmap2D pixmap(sm.getScreenW(), sm.getScreenH(), 3);
|
||||
if( pixmapScreenShot == NULL ||
|
||||
pixmapScreenShot->getW() != sm.getScreenW() ||
|
||||
pixmapScreenShot->getH() != sm.getScreenH()) {
|
||||
SystemFlags::OutputDebug(SystemFlags::debugSystem,"In [%s::%s Line: %d]\n",__FILE__,__FUNCTION__,__LINE__);
|
||||
|
||||
delete pixmapScreenShot;
|
||||
pixmapScreenShot = new Pixmap2D(sm.getScreenW(), sm.getScreenH(), 3);
|
||||
}
|
||||
Pixmap2D *pixmapScreenShot = new Pixmap2D(sm.getScreenW(), sm.getScreenH(), 3);
|
||||
|
||||
SystemFlags::OutputDebug(SystemFlags::debugSystem,"In [%s::%s Line: %d]\n",__FILE__,__FUNCTION__,__LINE__);
|
||||
glFinish();
|
||||
|
@ -2969,11 +3002,23 @@ void Renderer::saveScreen(const string &path){
|
|||
GL_RGB, GL_UNSIGNED_BYTE, pixmapScreenShot->getPixels());
|
||||
|
||||
SystemFlags::OutputDebug(SystemFlags::debugSystem,"In [%s::%s Line: %d]\n",__FILE__,__FUNCTION__,__LINE__);
|
||||
pixmapScreenShot->save(path);
|
||||
|
||||
// Signal the threads queue to add a screenshot save request
|
||||
MutexSafeWrapper safeMutex(&saveScreenShotThreadAccessor);
|
||||
saveScreenQueue.push_back(make_pair(path,pixmapScreenShot));
|
||||
safeMutex.ReleaseLock();
|
||||
|
||||
SystemFlags::OutputDebug(SystemFlags::debugSystem,"In [%s::%s Line: %d]\n",__FILE__,__FUNCTION__,__LINE__);
|
||||
}
|
||||
|
||||
unsigned int Renderer::getSaveScreenQueueSize() {
|
||||
MutexSafeWrapper safeMutex(&saveScreenShotThreadAccessor);
|
||||
int queueSize = saveScreenQueue.size();
|
||||
safeMutex.ReleaseLock();
|
||||
|
||||
return queueSize;
|
||||
}
|
||||
|
||||
// ==================== PRIVATE ====================
|
||||
|
||||
float Renderer::computeSunAngle(float time) {
|
||||
|
|
|
@ -31,6 +31,7 @@
|
|||
#include "model.h"
|
||||
#include "graphics_interface.h"
|
||||
#include "base_renderer.h"
|
||||
#include "simple_threads.h"
|
||||
|
||||
#ifdef DEBUG_RENDERING_ENABLED
|
||||
# define IF_DEBUG_EDITION(x) x
|
||||
|
@ -44,6 +45,7 @@
|
|||
namespace Glest{ namespace Game{
|
||||
|
||||
using namespace Shared::Graphics;
|
||||
using namespace Shared::PlatformCommon;
|
||||
|
||||
// =====================================================
|
||||
// class MeshCallbackTeamColor
|
||||
|
@ -131,7 +133,7 @@ public:
|
|||
};
|
||||
|
||||
|
||||
class Renderer : public RendererInterface, public BaseRenderer {
|
||||
class Renderer : public RendererInterface, public BaseRenderer, public SimpleTaskCallbackInterface {
|
||||
public:
|
||||
//progress bar
|
||||
static const int maxProgressBar;
|
||||
|
@ -242,7 +244,9 @@ private:
|
|||
|
||||
bool useQuadCache;
|
||||
|
||||
Pixmap2D *pixmapScreenShot;
|
||||
SimpleTaskThread *saveScreenShotThread;
|
||||
Mutex saveScreenShotThreadAccessor;
|
||||
std::list<std::pair<string,Pixmap2D *> > saveScreenQueue;
|
||||
|
||||
private:
|
||||
Renderer();
|
||||
|
@ -386,6 +390,7 @@ public:
|
|||
void removeUnitFromQuadCache(const Unit *unit);
|
||||
|
||||
uint64 getCurrentPixelByteCount(ResourceScope rs=rsGame) const;
|
||||
unsigned int getSaveScreenQueueSize();
|
||||
|
||||
private:
|
||||
//private misc
|
||||
|
@ -421,6 +426,8 @@ private:
|
|||
void renderTile(const Vec2i &pos);
|
||||
void renderQuad(int x, int y, int w, int h, const Texture2D *texture);
|
||||
|
||||
void simpleTask();
|
||||
|
||||
//static
|
||||
static Texture2D::Filter strToTextureFilter(const string &s);
|
||||
};
|
||||
|
|
|
@ -33,7 +33,7 @@ namespace Shared{ namespace Graphics{
|
|||
// class PixmapIo
|
||||
// =====================================================
|
||||
|
||||
class PixmapIo{
|
||||
class PixmapIo {
|
||||
protected:
|
||||
int w;
|
||||
int h;
|
||||
|
@ -58,7 +58,7 @@ public:
|
|||
// class PixmapIoTga
|
||||
// =====================================================
|
||||
|
||||
class PixmapIoTga: public PixmapIo{
|
||||
class PixmapIoTga: public PixmapIo {
|
||||
private:
|
||||
FILE *file;
|
||||
|
||||
|
@ -78,7 +78,7 @@ public:
|
|||
// class PixmapIoBmp
|
||||
// =====================================================
|
||||
|
||||
class PixmapIoBmp: public PixmapIo{
|
||||
class PixmapIoBmp: public PixmapIo {
|
||||
private:
|
||||
FILE *file;
|
||||
|
||||
|
@ -94,6 +94,27 @@ public:
|
|||
virtual void write(uint8 *pixels);
|
||||
};
|
||||
|
||||
// =====================================================
|
||||
// class PixmapIoBmp
|
||||
// =====================================================
|
||||
|
||||
class PixmapIoPng: public PixmapIo {
|
||||
private:
|
||||
FILE *file;
|
||||
string path;
|
||||
|
||||
public:
|
||||
PixmapIoPng();
|
||||
virtual ~PixmapIoPng();
|
||||
|
||||
virtual void openRead(const string &path);
|
||||
virtual void read(uint8 *pixels);
|
||||
virtual void read(uint8 *pixels, int components);
|
||||
|
||||
virtual void openWrite(const string &path, int w, int h, int components);
|
||||
virtual void write(uint8 *pixels);
|
||||
};
|
||||
|
||||
// =====================================================
|
||||
// class Pixmap1D
|
||||
// =====================================================
|
||||
|
@ -157,7 +178,7 @@ public:
|
|||
void save(const string &path);
|
||||
void saveBmp(const string &path);
|
||||
void saveTga(const string &path);
|
||||
|
||||
void savePng(const string &path);
|
||||
|
||||
//get
|
||||
int getW() const {return w;}
|
||||
|
|
|
@ -21,6 +21,8 @@
|
|||
#include "randomgen.h"
|
||||
#include "FileReader.h"
|
||||
#include "ImageReaders.h"
|
||||
#include <png.h>
|
||||
#include <setjmp.h>
|
||||
|
||||
#include "leak_dumper.h"
|
||||
|
||||
|
@ -285,7 +287,7 @@ void PixmapIoBmp::openWrite(const string &path, int w, int h, int components) {
|
|||
|
||||
file= fopen(path.c_str(),"wb");
|
||||
if (file == NULL) {
|
||||
throw runtime_error("Can't open BMP file for writting: "+ path);
|
||||
throw runtime_error("Can't open BMP file for writing: "+ path);
|
||||
}
|
||||
|
||||
BitmapFileHeader fileHeader;
|
||||
|
@ -321,6 +323,210 @@ void PixmapIoBmp::write(uint8 *pixels) {
|
|||
}
|
||||
}
|
||||
|
||||
// =====================================================
|
||||
// class PixmapIoPng
|
||||
// =====================================================
|
||||
|
||||
PixmapIoPng::PixmapIoPng() {
|
||||
file= NULL;
|
||||
}
|
||||
|
||||
PixmapIoPng::~PixmapIoPng() {
|
||||
if(file!=NULL){
|
||||
fclose(file);
|
||||
file=NULL;
|
||||
}
|
||||
}
|
||||
|
||||
void PixmapIoPng::openRead(const string &path) {
|
||||
|
||||
throw runtime_error("PixmapIoPng::openRead not implemented!");
|
||||
|
||||
/*
|
||||
file= fopen(path.c_str(),"rb");
|
||||
if (file==NULL){
|
||||
throw runtime_error("Can't open BMP file: "+ path);
|
||||
}
|
||||
|
||||
//read file header
|
||||
BitmapFileHeader fileHeader;
|
||||
size_t readBytes = fread(&fileHeader, sizeof(BitmapFileHeader), 1, file);
|
||||
if(fileHeader.type1!='B' || fileHeader.type2!='M'){
|
||||
throw runtime_error(path +" is not a bitmap");
|
||||
}
|
||||
|
||||
//read info header
|
||||
BitmapInfoHeader infoHeader;
|
||||
readBytes = fread(&infoHeader, sizeof(BitmapInfoHeader), 1, file);
|
||||
if(infoHeader.bitCount!=24){
|
||||
throw runtime_error(path+" is not a 24 bit bitmap");
|
||||
}
|
||||
|
||||
h= infoHeader.height;
|
||||
w= infoHeader.width;
|
||||
components= 3;
|
||||
*/
|
||||
}
|
||||
|
||||
void PixmapIoPng::read(uint8 *pixels) {
|
||||
throw runtime_error("PixmapIoPng::read not implemented!");
|
||||
//read(pixels, 3);
|
||||
}
|
||||
|
||||
void PixmapIoPng::read(uint8 *pixels, int components) {
|
||||
|
||||
throw runtime_error("PixmapIoPng::read #2 not implemented!");
|
||||
|
||||
/*
|
||||
for(int i=0; i<h*w*components; i+=components) {
|
||||
uint8 r, g, b;
|
||||
size_t readBytes = fread(&b, 1, 1, file);
|
||||
readBytes = fread(&g, 1, 1, file);
|
||||
readBytes = fread(&r, 1, 1, file);
|
||||
|
||||
switch(components){
|
||||
case 1:
|
||||
pixels[i]= (r+g+b)/3;
|
||||
break;
|
||||
case 3:
|
||||
pixels[i]= r;
|
||||
pixels[i+1]= g;
|
||||
pixels[i+2]= b;
|
||||
break;
|
||||
case 4:
|
||||
pixels[i]= r;
|
||||
pixels[i+1]= g;
|
||||
pixels[i+2]= b;
|
||||
pixels[i+3]= 255;
|
||||
break;
|
||||
}
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
void PixmapIoPng::openWrite(const string &path, int w, int h, int components) {
|
||||
this->path = path;
|
||||
this->w= w;
|
||||
this->h= h;
|
||||
this->components= components;
|
||||
|
||||
file= fopen(path.c_str(),"wb");
|
||||
if (file == NULL) {
|
||||
throw runtime_error("Can't open PNG file for writing: "+ path);
|
||||
}
|
||||
}
|
||||
|
||||
void PixmapIoPng::write(uint8 *pixels) {
|
||||
// Allocate write & info structures
|
||||
png_structp png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
|
||||
if(!png_ptr) {
|
||||
fclose(file);
|
||||
throw runtime_error("OpenGlDevice::saveImageAsPNG() - out of memory creating write structure");
|
||||
}
|
||||
|
||||
png_infop info_ptr = png_create_info_struct(png_ptr);
|
||||
if(!info_ptr) {
|
||||
png_destroy_write_struct(&png_ptr,
|
||||
(png_infopp)NULL);
|
||||
fclose(file);
|
||||
throw runtime_error("OpenGlDevice::saveImageAsPNG() - out of memery creating info structure");
|
||||
}
|
||||
|
||||
// setjmp() must be called in every function that calls a PNG-writing
|
||||
// libpng function, unless an alternate error handler was installed--
|
||||
// but compatible error handlers must either use longjmp() themselves
|
||||
// (as in this program) or exit immediately, so here we go: */
|
||||
|
||||
if(setjmp(png_jmpbuf(png_ptr))) {
|
||||
png_destroy_write_struct(&png_ptr, &info_ptr);
|
||||
fclose(file);
|
||||
throw runtime_error("OpenGlDevice::saveImageAsPNG() - setjmp problem");
|
||||
}
|
||||
|
||||
// make sure outfile is (re)opened in BINARY mode
|
||||
png_init_io(png_ptr, file);
|
||||
|
||||
// set the compression levels--in general, always want to leave filtering
|
||||
// turned on (except for palette images) and allow all of the filters,
|
||||
// which is the default; want 32K zlib window, unless entire image buffer
|
||||
// is 16K or smaller (unknown here)--also the default; usually want max
|
||||
// compression (NOT the default); and remaining compression flags should
|
||||
// be left alone
|
||||
|
||||
//png_set_compression_level(png_ptr, Z_BEST_COMPRESSION);
|
||||
png_set_compression_level(png_ptr, Z_DEFAULT_COMPRESSION);
|
||||
|
||||
//
|
||||
// this is default for no filtering; Z_FILTERED is default otherwise:
|
||||
// png_set_compression_strategy(png_ptr, Z_DEFAULT_STRATEGY);
|
||||
// these are all defaults:
|
||||
// png_set_compression_mem_level(png_ptr, 8);
|
||||
// png_set_compression_window_bits(png_ptr, 15);
|
||||
// png_set_compression_method(png_ptr, 8);
|
||||
|
||||
|
||||
// Set some options: color_type, interlace_type
|
||||
int color_type=0, interlace_type=0, numChannels=0;
|
||||
|
||||
// color_type = PNG_COLOR_TYPE_GRAY;
|
||||
// color_type = PNG_COLOR_TYPE_GRAY_ALPHA;
|
||||
color_type = PNG_COLOR_TYPE_RGB;
|
||||
numChannels = 3;
|
||||
// color_type = PNG_COLOR_TYPE_RGB_ALPHA;
|
||||
|
||||
interlace_type = PNG_INTERLACE_NONE;
|
||||
// interlace_type = PNG_INTERLACE_ADAM7;
|
||||
|
||||
int bit_depth = 8;
|
||||
png_set_IHDR(png_ptr, info_ptr, this->w, this->h, bit_depth,
|
||||
color_type,
|
||||
interlace_type,
|
||||
PNG_COMPRESSION_TYPE_BASE,
|
||||
PNG_FILTER_TYPE_BASE);
|
||||
|
||||
// Optional gamma chunk is strongly suggested if you have any guess
|
||||
// as to the correct gamma of the image. (we don't have a guess)
|
||||
//
|
||||
// png_set_gAMA(png_ptr, info_ptr, image_gamma);
|
||||
|
||||
// write all chunks up to (but not including) first IDAT
|
||||
png_write_info(png_ptr, info_ptr);
|
||||
|
||||
// set up the row pointers for the image so we can use png_write_image
|
||||
|
||||
png_bytep* row_pointers = new png_bytep[this->h];
|
||||
if (row_pointers == 0) {
|
||||
png_destroy_write_struct(&png_ptr, &info_ptr);
|
||||
fclose(file);
|
||||
throw runtime_error("OpenGlDevice::failed to allocate memory for row pointers");
|
||||
}
|
||||
|
||||
unsigned int row_stride = this->w * numChannels;
|
||||
unsigned char *rowptr = (unsigned char*) pixels;
|
||||
for (int row = this->h-1; row >=0 ; row--) {
|
||||
row_pointers[row] = rowptr;
|
||||
rowptr += row_stride;
|
||||
}
|
||||
|
||||
// now we just write the whole image; libpng takes care of interlacing for us
|
||||
png_write_image(png_ptr, row_pointers);
|
||||
|
||||
// since that's it, we also close out the end of the PNG file now--if we
|
||||
// had any text or time info to write after the IDATs, second argument
|
||||
// would be info_ptr, but we optimize slightly by sending NULL pointer: */
|
||||
|
||||
png_write_end(png_ptr, info_ptr);
|
||||
|
||||
//
|
||||
// clean up after the write
|
||||
// free any memory allocated & close the file
|
||||
//
|
||||
png_destroy_write_struct(&png_ptr, &info_ptr);
|
||||
|
||||
delete [] row_pointers;
|
||||
//fclose(file);
|
||||
}
|
||||
|
||||
// =====================================================
|
||||
// class Pixmap1D
|
||||
// =====================================================
|
||||
|
@ -512,6 +718,9 @@ void Pixmap2D::save(const string &path) {
|
|||
else if(toLower(extension) == "tga") {
|
||||
saveTga(path);
|
||||
}
|
||||
else if(toLower(extension) == "png") {
|
||||
savePng(path);
|
||||
}
|
||||
else {
|
||||
throw runtime_error("Unknown pixmap extension: " + extension);
|
||||
}
|
||||
|
@ -528,7 +737,12 @@ void Pixmap2D::saveTga(const string &path) {
|
|||
pst.openWrite(path, w, h, components);
|
||||
pst.write(pixels);
|
||||
}
|
||||
void Pixmap2D::savePng(const string &path) {
|
||||
PixmapIoPng pst;
|
||||
pst.openWrite(path, w, h, components);
|
||||
pst.write(pixels);
|
||||
|
||||
}
|
||||
void Pixmap2D::getPixel(int x, int y, uint8 *value) const {
|
||||
for(int i=0; i<components; ++i){
|
||||
value[i]= pixels[(w*y+x)*components+i];
|
||||
|
|
Loading…
Reference in New Issue
Block a user