diff --git a/include/mitsuba/core/bitmap.h b/include/mitsuba/core/bitmap.h
index fddc1dca..2d14c3fb 100644
--- a/include/mitsuba/core/bitmap.h
+++ b/include/mitsuba/core/bitmap.h
@@ -223,6 +223,16 @@ public:
*/
EPFM,
+ /**
+ * \brief PPM (Portable Pixel Map) image format
+ *
+ * The following is supported
+ *
+ * - Loading and saving of \ref EUInt8 and \ref EUInt16 - based RGB bitmaps
+ *
+ */
+ EPPM,
+
/**
* \brief Joint Photographic Experts Group file format
*
@@ -1115,6 +1125,12 @@ protected:
/// Write a file using the PFM file format
void writePFM(Stream *stream) const;
+ /// Read a file stored using the PPM file format
+ void readPPM(Stream *stream);
+
+ /// Write a file using the PPM file format
+ void writePPM(Stream *stream) const;
+
/// Read a file stored using the TGA file format
void readTGA(Stream *stream);
diff --git a/src/libcore/bitmap.cpp b/src/libcore/bitmap.cpp
index ace83d15..b89d6185 100644
--- a/src/libcore/bitmap.cpp
+++ b/src/libcore/bitmap.cpp
@@ -305,6 +305,8 @@ Bitmap::Bitmap(EFileFormat format, Stream *stream, const std::string &prefix) :
format = ERGBE;
} else if (start[0] == 'P' && (start[1] == 'F' || start[1] == 'f')) {
format = EPFM;
+ } else if (start[0] == 'P' && start[1] == '6') {
+ format = EPPM;
#if defined(MTS_HAS_LIBJPEG)
} else if (start[0] == 0xFF && start[1] == 0xD8) {
format = EJPEG;
@@ -334,6 +336,7 @@ Bitmap::Bitmap(EFileFormat format, Stream *stream, const std::string &prefix) :
case EOpenEXR: readOpenEXR(stream, prefix); break;
case ERGBE: readRGBE(stream); break;
case EPFM: readPFM(stream); break;
+ case EPPM: readPPM(stream); break;
case ETGA: readTGA(stream); break;
case EPNG: readPNG(stream); break;
default:
@@ -356,6 +359,7 @@ void Bitmap::write(EFileFormat format, Stream *stream, int compression) const {
case EOpenEXR: writeOpenEXR(stream); break;
case ERGBE: writeRGBE(stream); break;
case EPFM: writePFM(stream); break;
+ case EPPM: writePPM(stream); break;
default:
Log(EError, "Bitmap::write(): Invalid file format!");
}
@@ -3402,6 +3406,54 @@ void Bitmap::writePFM(Stream *stream) const {
}
}
+void Bitmap::readPPM(Stream *stream) {
+ int field = 0, nChars = 0;
+
+ std::string fields[4];
+
+ while (field < 4) {
+ char c = stream->readChar();
+ if (c == ' ' || c == '\t' || c == '\n' || c == '\r') {
+ if (nChars != 0) {
+ nChars = 0;
+ ++field;
+ }
+ } else {
+ fields[field] += c;
+ ++nChars;
+ }
+ }
+ if (fields[0] != "P6")
+ Log(EError, "readPPM(): invalid format!");
+
+ int intValues[3];
+ for (int i=0; i<3; ++i) {
+ char *end_ptr = NULL;
+ intValues[i] = strtol(fields[i+1].c_str(), &end_ptr, 10);
+ if (*end_ptr != '\0')
+ SLog(EError, "readPPM(): unable to parse the file header!");
+ }
+
+ m_size.x = intValues[0];
+ m_size.y = intValues[1];
+ m_pixelFormat = ERGB;
+ m_channelCount = 3;
+ m_gamma = -1.0f;
+ m_ownsData = true;
+ m_componentFormat = intValues[2] <= 0xFF ? EUInt8 : EUInt16;
+ size_t size = getBufferSize();
+ m_data = static_cast(allocAligned(size));
+ stream->read(m_data, size);
+}
+
+void Bitmap::writePPM(Stream *stream) const {
+ if (m_pixelFormat != ERGB || (m_componentFormat != EUInt8 && m_componentFormat != EUInt16))
+ Log(EError, "writePPM(): Only 8 or 16-bit RGB images are supported");
+ stream->writeLine(formatString("P6\n%i\n%i\n%i\n", m_size.x, m_size.y,
+ m_componentFormat == EUInt8 ? 0xFF : 0xFFFF).c_str());
+ stream->write(m_data, getBufferSize());
+}
+
void Bitmap::staticInitialization() {
#if defined(MTS_HAS_OPENEXR)
/* Prevent races during the OpenEXR initialization */
diff --git a/src/libpython/core.cpp b/src/libpython/core.cpp
index 321536c1..32454095 100644
--- a/src/libpython/core.cpp
+++ b/src/libpython/core.cpp
@@ -1364,6 +1364,7 @@ void export_core() {
.value("EOpenEXR", Bitmap::EOpenEXR)
.value("ETGA", Bitmap::ETGA)
.value("EPFM", Bitmap::EPFM)
+ .value("EPPM", Bitmap::EPPM)
.value("ERGBE", Bitmap::ERGBE)
.value("EBMP", Bitmap::EBMP)
.value("EJPEG", Bitmap::EJPEG)
diff --git a/src/mtsgui/mainwindow.cpp b/src/mtsgui/mainwindow.cpp
index cdbc28b4..2fc404bf 100644
--- a/src/mtsgui/mainwindow.cpp
+++ b/src/mtsgui/mainwindow.cpp
@@ -585,8 +585,8 @@ void MainWindow::on_actionOpen_triggered() {
QSettings settings;
QStringList fileNames = QFileDialog::getOpenFileNames(this, QString(),
settings.value("fileDir").toString(),
- tr("All supported formats (*.xml *.exr *.rgbe *.hdr *.pfm *.png *.jpg *.jpeg);;"
- "Mitsuba scenes (*.xml);;High dynamic-range images (*.exr *.rgbe *.hdr *.pfm);;"
+ tr("All supported formats (*.xml *.exr *.rgbe *.hdr *.pfm *.ppm *.png *.jpg *.jpeg);;"
+ "Mitsuba scenes (*.xml);;High dynamic-range images (*.exr *.rgbe *.hdr *.pfm *.ppm);;"
"Low dynamic-range images (*.png *.jpg *.jpeg)"));
QStringList::ConstIterator it = fileNames.constBegin();
@@ -605,8 +605,8 @@ void MainWindow::onOpenDialogClose(int reason) { /* unused */ }
void MainWindow::on_actionOpen_triggered() {
QFileDialog *dialog = new QFileDialog(this, Qt::Sheet);
- dialog->setNameFilter(tr("All supported formats (*.xml *.exr *.rgbe *.hdr *.pfm *.png *.jpg *.jpeg);;"
- "Mitsuba scenes (*.xml);;High dynamic-range images (*.exr *.rgbe *.hdr *.pfm);;Low "
+ dialog->setNameFilter(tr("All supported formats (*.xml *.exr *.rgbe *.hdr *.pfm *.ppm *.png *.jpg *.jpeg);;"
+ "Mitsuba scenes (*.xml);;High dynamic-range images (*.exr *.rgbe *.hdr *.pfm *.ppm);;Low "
"dynamic-range images (*.png *.jpg *.jpeg)"));
dialog->setAttribute(Qt::WA_DeleteOnClose);
dialog->setAcceptMode(QFileDialog::AcceptOpen);
@@ -1498,6 +1498,7 @@ void MainWindow::on_actionExportImage_triggered() {
"High dynamic range OpenEXR image (*.exr);;"
"High dynamic range Radiance RGBE image (*.rgbe *.hdr);;"
"High dynamic range Portable Float Map image (*.pfm);;"
+ "High dynamic range Portable Pixel Map image (*.ppm);;"
"Tonemapped low dynamic range image (*.png *.jpg *.jpeg)"));
if (!fileName.isEmpty()) {
QSettings settings;
@@ -1516,6 +1517,7 @@ void MainWindow::on_actionExportImage_triggered() {
"High dynamic range OpenEXR image (*.exr);;"
"High dynamic range Radiance RGBE image (*.rgbe *.hdr);;"
"High dynamic range Portable Float Map image (*.pfm);;"
+ "High dynamic range Portable Pixel Map image (*.ppm);;"
"Tonemapped low dynamic range image (*.png *.jpg *.jpeg)"));
QSettings settings;
@@ -1554,23 +1556,27 @@ void MainWindow::onExportDialogClose(int reason) {
void MainWindow::exportImage(const QString &fileName) {
if (!fileName.isEmpty()) {
+ Bitmap::EComponentFormat compFormat = Bitmap::EInvalid;
Bitmap::EFileFormat format;
- bool isHDR = true;
+
if (fileName.endsWith(".exr")) {
format = Bitmap::EOpenEXR;
} else if (fileName.endsWith(".png") || fileName == "__clipboard__") {
format = Bitmap::EPNG;
- isHDR = false;
+ compFormat = Bitmap::EUInt8;
} else if (fileName.endsWith(".hdr") || fileName.endsWith(".rgbe")) {
format = Bitmap::ERGBE;
} else if (fileName.endsWith(".jpg") || fileName.endsWith(".jpeg")) {
format = Bitmap::EJPEG;
- isHDR = false;
+ compFormat = Bitmap::EUInt8;
} else if (fileName.endsWith(".pfm")) {
format = Bitmap::EPFM;
+ } else if (fileName.endsWith(".ppm")) {
+ format = Bitmap::EPPM;
+ compFormat = Bitmap::EUInt16;
} else {
SLog(EError, "Unknown file type -- the filename must end in either"
- " .exr, .rgbe, .hdr, .pfm, .png, .jpg, or .jpeg");
+ " .exr, .rgbe, .hdr, .pfm, .ppm, .png, .jpg, or .jpeg");
return;
}
@@ -1581,7 +1587,7 @@ void MainWindow::exportImage(const QString &fileName) {
ui->glView->downloadFramebuffer();
ref bitmap = ctx->framebuffer;
- if (!isHDR) {
+ if (compFormat == Bitmap::EUInt8 || compFormat == Bitmap::EUInt16) {
/* Tonemap the image */
if (ctx->toneMappingMethod == EReinhard) {
Float logAvgLuminance = 0, maxLuminance = 0; /* Unused */
@@ -1592,7 +1598,7 @@ void MainWindow::exportImage(const QString &fileName) {
ctx->reinhardKey, burn);
}
- bitmap = bitmap->convert(Bitmap::ERGB, Bitmap::EUInt8,
+ bitmap = bitmap->convert(Bitmap::ERGB, compFormat,
ctx->srgb ? (Float) -1 : ctx->gamma,
ctx->toneMappingMethod == EReinhard
? (Float) 1.0f : std::pow((Float) 2, ctx->exposure));
diff --git a/src/mtsgui/sceneloader.cpp b/src/mtsgui/sceneloader.cpp
index f1deea52..bc5459fa 100644
--- a/src/mtsgui/sceneloader.cpp
+++ b/src/mtsgui/sceneloader.cpp
@@ -67,7 +67,7 @@ void SceneLoader::run() {
m_result->diffuseReceivers = settings.value("preview_diffuseReceivers", false).toBool();
if (suffix == "exr" || suffix == "png" || suffix == "jpg" || suffix == "jpeg" ||
- suffix == "hdr" || suffix == "rgbe" || suffix == "pfm") {
+ suffix == "hdr" || suffix == "rgbe" || suffix == "pfm" || suffix == "ppm") {
/* This is an image, not a scene */
ref fs = new FileStream(toFsPath(m_filename), FileStream::EReadOnly);
ref bitmap = new Bitmap(Bitmap::EAuto, fs);