Restored final picture saving (in constant memory usage)

This commit is contained in:
Michaël Lemaire 2014-08-21 12:36:28 +02:00
parent 6c4a16966c
commit c39ef6adce
18 changed files with 296 additions and 9 deletions

View file

@ -8,6 +8,7 @@
#include "SoftwareCanvasRenderer.h"
#include "Scenery.h"
#include "RenderConfig.h"
#include "ColorProfile.h"
void startRender(SoftwareCanvasRenderer *renderer, char *outputpath)
{
@ -15,7 +16,7 @@ void startRender(SoftwareCanvasRenderer *renderer, char *outputpath)
renderer->render();
printf("\rSaving %s ... \n", outputpath);
remove(outputpath);
//renderer->render_area->saveToFile(outputpath);
renderer->saveToDisk(outputpath);
}
void displayHelp()

View file

@ -22,6 +22,7 @@
#include "ColorProfile.h"
#include "SoftwareCanvasRenderer.h"
#include "WidgetPreviewCanvas.h"
#include "Canvas.h"
class RenderThread:public QThread
{
@ -150,12 +151,11 @@ void DialogRender::saveRender()
{
filepath = filepath.append(".png");
}
std::string filepathstr = filepath.toStdString();
/*if (canvas_renderer->render_area->saveToFile((char*)filepathstr.c_str()))
if (canvas_renderer->saveToDisk(filepath.toStdString()))
{
QMessageBox::information(this, "Message", QString(tr("The picture %1 has been saved.")).arg(filepath));
}
else*/
else
{
QMessageBox::critical(this, "Message", QString(tr("Can't write to file : %1")).arg(filepath));
}

View file

@ -4,6 +4,7 @@
#include "CanvasPortion.h"
#include "CanvasPreview.h"
#include "CanvasPictureWriter.h"
Canvas::Canvas()
{
@ -86,3 +87,24 @@ CanvasPortion *Canvas::at(int x, int y) const
return portions[y * horizontal_portion_count + x];
}
CanvasPortion *Canvas::atPixel(int x, int y) const
{
assert(x >= 0 && x < width);
assert(y >= 0 && y < height);
int pwidth = portions[0]->getWidth();
int pheight = portions[0]->getHeight();
return at(x / pwidth, y / pheight);
}
bool Canvas::saveToDisk(const std::string &filepath, const ColorProfile &profile, int antialias) const
{
assert(antialias >= 1);
CanvasPictureWriter writer(this);
writer.setColorProfile(profile);
writer.setAntialias(antialias);
return writer.saveCanvas(filepath);
}

View file

@ -24,11 +24,19 @@ public:
inline int getVerticalPortionCount() const {return vertical_portion_count;}
CanvasPortion *at(int x, int y) const;
CanvasPortion *atPixel(int x, int y) const;
inline int getWidth() const {return width;}
inline int getHeight() const {return height;}
inline CanvasPreview *getPreview() const {return preview;}
/**
* Save the canvas to a picture file on disk.
*
* Returns true if the save was successful.
*/
bool saveToDisk(const std::string &filepath, const ColorProfile &profile, int antialias) const;
private:
std::vector<CanvasPortion*> portions;
int horizontal_portion_count;

View file

@ -0,0 +1,89 @@
#include "CanvasPictureWriter.h"
#include <cassert>
#include "Canvas.h"
#include "CanvasPortion.h"
#include "ColorProfile.h"
#include "PackStream.h"
CanvasPictureWriter::CanvasPictureWriter(const Canvas *canvas):
canvas(canvas)
{
profile = new ColorProfile();
antialias = 1;
width = canvas->getWidth();
height = canvas->getHeight();
}
CanvasPictureWriter::~CanvasPictureWriter()
{
delete profile;
}
void CanvasPictureWriter::setAntialias(int antialias)
{
assert(antialias >= 1);
assert(canvas->getWidth() % antialias == 0);
assert(canvas->getHeight() % antialias == 0);
this->antialias = antialias;
this->width = canvas->getWidth() / antialias;
this->height = canvas->getHeight() / antialias;
}
void CanvasPictureWriter::setColorProfile(const ColorProfile &profile)
{
profile.copy(this->profile);
}
bool CanvasPictureWriter::saveCanvas(const std::string &filepath)
{
return save(filepath, width, height);
}
unsigned int CanvasPictureWriter::getPixel(int x, int y)
{
if (antialias > 1)
{
int basex = x * antialias;
int basey = y * antialias;
double factor = 1.0 / (antialias * antialias);
Color comp = COLOR_BLACK;
for (int ix = 0; ix < antialias; ix++)
{
for (int iy = 0; iy < antialias; iy++)
{
Color col = getRawPixel(basex + ix, basey + iy);
comp.r += col.r * factor;
comp.g += col.g * factor;
comp.b += col.b * factor;
}
}
return profile->apply(comp).to32BitBGRA();
}
else
{
return profile->apply(getRawPixel(x, y)).to32BitBGRA();
}
}
Color CanvasPictureWriter::getRawPixel(int x, int y)
{
// Get the portion this pixel is in
CanvasPortion *portion = canvas->atPixel(x, y);
// Get the pack stream positioned at the pixel
PackStream stream;
if (not portion->getReadStream(stream, x - portion->getXOffset(), y - portion->getYOffset()))
{
return COLOR_BLACK;
}
// Load the pixel and apply tone mapping
Color col;
col.load(&stream);
return col;
}

View file

@ -0,0 +1,54 @@
#ifndef CANVASPICTUREWRITER_H
#define CANVASPICTUREWRITER_H
#include "software_global.h"
#include "PictureWriter.h"
namespace paysages {
namespace software {
/**
* Picture writer to create the final image from canvas portions.
*/
class SOFTWARESHARED_EXPORT CanvasPictureWriter: public PictureWriter
{
public:
CanvasPictureWriter(const Canvas *canvas);
virtual ~CanvasPictureWriter();
/**
* Set the antialias factor, 1 for no antialiasing.
*/
void setAntialias(int antialias);
/**
* Set the color profile to apply to final pixels.
*/
void setColorProfile(const ColorProfile &profile);
/**
* Start the saving process.
*
* Returns true if saving was successful.
*/
bool saveCanvas(const std::string &filepath);
protected:
virtual unsigned int getPixel(int x, int y) override;
private:
Color getRawPixel(int x, int y);
private:
const Canvas *canvas;
int antialias;
int width;
int height;
ColorProfile *profile;
};
}
}
#endif // CANVASPICTUREWRITER_H

View file

@ -89,14 +89,14 @@ void CanvasPortion::saveToDisk()
{
if (pixels)
{
std::string filepath = FileSystem::getTempFile("paysages_portion_" + std::to_string(index) + ".dat");
filepath = FileSystem::getTempFile("paysages_portion_" + std::to_string(index) + ".dat");
PackStream stream;
stream.bindToFile(filepath, true);
stream.write(&width);
stream.write(&height);
for (int x = 0; x < width; x++)
for (int y = 0; y < height; y++)
{
for (int y = 0; y < height; y++)
for (int x = 0; x < width; x++)
{
pixels[y * width + x].getComposite().save(&stream);
}
@ -104,6 +104,23 @@ void CanvasPortion::saveToDisk()
}
}
bool CanvasPortion::getReadStream(PackStream &stream, int x, int y)
{
if (FileSystem::isFile(filepath))
{
int unused_i;
double unused_d;
stream.bindToFile(filepath);
stream.skip(unused_i, 2);
stream.skip(unused_d, (y * width + x - 1) * 4);
return true;
}
else
{
return false;
}
}
void CanvasPortion::pushFragment(int x, int y, const CanvasFragment &fragment)
{
CHECK_COORDINATES();

View file

@ -46,6 +46,13 @@ public:
*/
void saveToDisk();
/**
* Bind a stream to pixel data, and position it on a given pixel.
*
* Returns true if the stream was successfully located, false if it was not possible.
*/
bool getReadStream(PackStream &stream, int x=0, int y=0);
/**
* Add a fragment to the pixel located at (x, y).
*
@ -75,6 +82,7 @@ private:
int yoffset;
CanvasPixel *pixels;
CanvasPreview *preview;
std::string filepath;
};
}

View file

@ -17,6 +17,7 @@ public:
inline int getWidth() const {return width;}
inline int getHeight() const {return height;}
inline const ColorProfile *getToneMapping() const {return profile;}
const Color &getFinalPixel(int x, int y) const;

View file

@ -11,6 +11,8 @@
#include "CanvasPortion.h"
#include "CanvasPixelShader.h"
#include "RenderConfig.h"
#include "ColorProfile.h"
#include "CanvasPreview.h"
SoftwareCanvasRenderer::SoftwareCanvasRenderer()
{
@ -18,6 +20,7 @@ SoftwareCanvasRenderer::SoftwareCanvasRenderer()
interrupted = false;
canvas = new Canvas();
progress = 0.0;
samples = 1;
rasterizers.push_back(new SkyRasterizer(this, 0));
rasterizers.push_back(new WaterRasterizer(this, 1));
@ -50,6 +53,7 @@ void SoftwareCanvasRenderer::setSize(int width, int height, int samples)
if (not started)
{
canvas->setSize(width * samples, height * samples);
this->samples = samples;
}
}
@ -112,6 +116,11 @@ const Rasterizer &SoftwareCanvasRenderer::getRasterizer(int client_id) const
return *(rasterizers[client_id]);
}
bool SoftwareCanvasRenderer::saveToDisk(const std::string &filepath) const
{
return getCanvas()->saveToDisk(filepath, *getCanvas()->getPreview()->getToneMapping(), samples);
}
void SoftwareCanvasRenderer::rasterize(CanvasPortion *portion)
{
for (auto &rasterizer:rasterizers)

View file

@ -52,6 +52,13 @@ public:
*/
const Rasterizer &getRasterizer(int client_id) const;
/**
* Save the rendered canvas to a picture file on disk.
*
* Returns true if the save was successful.
*/
bool saveToDisk(const std::string &filepath) const;
protected:
/**
* @brief Rasterize the scenery into a canvas portion.
@ -68,6 +75,7 @@ private:
double progress_segment;
Canvas *canvas;
int samples;
std::vector<Rasterizer*> rasterizers;
bool started;
bool interrupted;

View file

@ -46,7 +46,8 @@ SOURCES += SoftwareRenderer.cpp \
CanvasLiveClient.cpp \
CanvasPreview.cpp \
RenderConfig.cpp \
CanvasPixelShader.cpp
CanvasPixelShader.cpp \
CanvasPictureWriter.cpp
HEADERS += SoftwareRenderer.h\
software_global.h \
@ -82,7 +83,8 @@ HEADERS += SoftwareRenderer.h\
CanvasLiveClient.h \
CanvasPreview.h \
RenderConfig.h \
CanvasPixelShader.h
CanvasPixelShader.h \
CanvasPictureWriter.h
unix:!symbian {
maemo5 {

View file

@ -54,6 +54,7 @@ namespace software {
class CanvasLiveClient;
class CanvasPreview;
class CanvasPixelShader;
class CanvasPictureWriter;
}
}

View file

@ -1,8 +1,14 @@
#include "FileSystem.h"
#include <QDir>
#include <QFileInfo>
std::string FileSystem::getTempFile(const std::string &filename)
{
return QDir::temp().filePath(QString::fromStdString(filename)).toStdString();
}
bool FileSystem::isFile(const std::string &filepath)
{
return QFileInfo(QString::fromStdString(filepath)).exists();
}

View file

@ -15,6 +15,11 @@ public:
* filename must not contain directory separators.
*/
static std::string getTempFile(const std::string &filename);
/**
* Returns true if the given path points to a file.
*/
static bool isFile(const std::string &filepath);
};
}

View file

@ -114,3 +114,13 @@ std::string PackStream::readString()
return "";
}
}
void PackStream::skip(const int &value, int count)
{
stream->skipRawData(sizeof(value) * count);
}
void PackStream::skip(const double &value, int count)
{
stream->skipRawData(sizeof(value) * count);
}

View file

@ -32,6 +32,9 @@ public:
void read(char* value, int max_length);
std::string readString();
void skip(const int &value, int count=1);
void skip(const double &value, int count=1);
private:
QFile* file;
QDataStream* stream;

View file

@ -49,3 +49,46 @@ TEST(PackStream, All)
}
delete stream;
}
TEST(PackStream, Skip)
{
PackStream* stream;
int i1=1, i2=2, i3=3;
double d1=1.1, d2=2.2;
stream = new PackStream();
stream->bindToFile("/tmp/test_paysages_pack", true);
stream->write(&i1);
stream->write(&i2);
stream->write(&d1);
stream->write(&d2);
stream->write(&i3);
delete stream;
int resi;
double resd;
stream = new PackStream();
stream->bindToFile("/tmp/test_paysages_pack");
stream->skip(i1, 1);
stream->read(&resi);
EXPECT_EQ(2, resi);
delete stream;
stream = new PackStream();
stream->bindToFile("/tmp/test_paysages_pack");
stream->skip(i1, 2);
stream->read(&resd);
EXPECT_DOUBLE_EQ(1.1, resd);
delete stream;
stream = new PackStream();
stream->bindToFile("/tmp/test_paysages_pack");
stream->skip(i1, 2);
stream->skip(d1, 2);
stream->read(&resi);
EXPECT_EQ(3, resi);
delete stream;
}