From 4b1abec7f6260ae92ab2fd4aeefde83cddd94197 Mon Sep 17 00:00:00 2001 From: Wenzel Jakob Date: Sun, 2 Mar 2014 21:06:25 +0100 Subject: [PATCH] added Bitmap I/O support for the PPM file format --- include/mitsuba/core/bitmap.h | 16 +++++++++++ src/libcore/bitmap.cpp | 52 +++++++++++++++++++++++++++++++++++ src/libpython/core.cpp | 1 + src/mtsgui/mainwindow.cpp | 26 +++++++++++------- src/mtsgui/sceneloader.cpp | 2 +- 5 files changed, 86 insertions(+), 11 deletions(-) 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 + * + */ + 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);