139 lines
5.0 KiB
C++
139 lines
5.0 KiB
C++
/*
|
|
This file is part of Nori, a simple educational ray tracer
|
|
|
|
Copyright (c) 2015 by Wenzel Jakob
|
|
|
|
Nori is free software; you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License Version 3
|
|
as published by the Free Software Foundation.
|
|
|
|
Nori is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#include <nori/bitmap.h>
|
|
#include <ImfInputFile.h>
|
|
#include <ImfOutputFile.h>
|
|
#include <ImfChannelList.h>
|
|
#include <ImfStringAttribute.h>
|
|
#include <ImfVersion.h>
|
|
#include <ImfIO.h>
|
|
#include <ctime>
|
|
|
|
#define STB_IMAGE_WRITE_IMPLEMENTATION
|
|
#include <stb_image_write.h>
|
|
|
|
NORI_NAMESPACE_BEGIN
|
|
|
|
Bitmap::Bitmap(const std::string &filename) {
|
|
Imf::InputFile file(filename.c_str());
|
|
const Imf::Header &header = file.header();
|
|
const Imf::ChannelList &channels = header.channels();
|
|
|
|
Imath::Box2i dw = file.header().dataWindow();
|
|
resize(dw.max.y - dw.min.y + 1, dw.max.x - dw.min.x + 1);
|
|
|
|
cout << "Reading a " << cols() << "x" << rows() << " OpenEXR file from \""
|
|
<< filename << "\"" << endl;
|
|
|
|
const char *ch_r = nullptr, *ch_g = nullptr, *ch_b = nullptr;
|
|
for (Imf::ChannelList::ConstIterator it = channels.begin(); it != channels.end(); ++it) {
|
|
std::string name = toLower(it.name());
|
|
|
|
if (it.channel().xSampling != 1 || it.channel().ySampling != 1) {
|
|
/* Sub-sampled layers are not supported */
|
|
continue;
|
|
}
|
|
|
|
if (!ch_r && (name == "r" || name == "red" ||
|
|
endsWith(name, ".r") || endsWith(name, ".red"))) {
|
|
ch_r = it.name();
|
|
} else if (!ch_g && (name == "g" || name == "green" ||
|
|
endsWith(name, ".g") || endsWith(name, ".green"))) {
|
|
ch_g = it.name();
|
|
} else if (!ch_b && (name == "b" || name == "blue" ||
|
|
endsWith(name, ".b") || endsWith(name, ".blue"))) {
|
|
ch_b = it.name();
|
|
}
|
|
}
|
|
|
|
if (!ch_r || !ch_g || !ch_b)
|
|
throw NoriException("This is not a standard RGB OpenEXR file!");
|
|
|
|
size_t compStride = sizeof(float),
|
|
pixelStride = 3 * compStride,
|
|
rowStride = pixelStride * cols();
|
|
|
|
char *ptr = reinterpret_cast<char *>(data());
|
|
|
|
Imf::FrameBuffer frameBuffer;
|
|
frameBuffer.insert(ch_r, Imf::Slice(Imf::FLOAT, ptr, pixelStride, rowStride)); ptr += compStride;
|
|
frameBuffer.insert(ch_g, Imf::Slice(Imf::FLOAT, ptr, pixelStride, rowStride)); ptr += compStride;
|
|
frameBuffer.insert(ch_b, Imf::Slice(Imf::FLOAT, ptr, pixelStride, rowStride));
|
|
file.setFrameBuffer(frameBuffer);
|
|
file.readPixels(dw.min.y, dw.max.y);
|
|
}
|
|
|
|
void Bitmap::saveEXR(const std::string &filename) {
|
|
cout << "Writing a " << cols() << "x" << rows()
|
|
<< " OpenEXR file to \"" << filename << "\"" << endl;
|
|
|
|
std::string path = filename + ".exr";
|
|
|
|
Imf::Header header((int) cols(), (int) rows());
|
|
header.insert("comments", Imf::StringAttribute("Generated by Nori"));
|
|
header.insert("id", Imf::StringAttribute(std::to_string(std::time(nullptr))));
|
|
|
|
Imf::ChannelList &channels = header.channels();
|
|
channels.insert("R", Imf::Channel(Imf::FLOAT));
|
|
channels.insert("G", Imf::Channel(Imf::FLOAT));
|
|
channels.insert("B", Imf::Channel(Imf::FLOAT));
|
|
|
|
Imf::FrameBuffer frameBuffer;
|
|
size_t compStride = sizeof(float),
|
|
pixelStride = 3 * compStride,
|
|
rowStride = pixelStride * cols();
|
|
|
|
char *ptr = reinterpret_cast<char *>(data());
|
|
frameBuffer.insert("R", Imf::Slice(Imf::FLOAT, ptr, pixelStride, rowStride)); ptr += compStride;
|
|
frameBuffer.insert("G", Imf::Slice(Imf::FLOAT, ptr, pixelStride, rowStride)); ptr += compStride;
|
|
frameBuffer.insert("B", Imf::Slice(Imf::FLOAT, ptr, pixelStride, rowStride));
|
|
|
|
Imf::OutputFile file(path.c_str(), header);
|
|
file.setFrameBuffer(frameBuffer);
|
|
file.writePixels((int) rows());
|
|
}
|
|
|
|
void Bitmap::savePNG(const std::string &filename) {
|
|
cout << "Writing a " << cols() << "x" << rows()
|
|
<< " PNG file to \"" << filename << "\"" << endl;
|
|
|
|
std::string path = filename + ".png";
|
|
|
|
uint8_t *rgb8 = new uint8_t[3 * cols() * rows()];
|
|
uint8_t *dst = rgb8;
|
|
for (int i = 0; i < rows(); ++i) {
|
|
for (int j = 0; j < cols(); ++j) {
|
|
Color3f tonemapped = coeffRef(i, j).toSRGB();
|
|
dst[0] = (uint8_t) clamp(255.f * tonemapped[0], 0.f, 255.f);
|
|
dst[1] = (uint8_t) clamp(255.f * tonemapped[1], 0.f, 255.f);
|
|
dst[2] = (uint8_t) clamp(255.f * tonemapped[2], 0.f, 255.f);
|
|
dst += 3;
|
|
}
|
|
}
|
|
|
|
int ret = stbi_write_png(path.c_str(), (int) cols(), (int) rows(), 3, rgb8, 3 * (int) cols());
|
|
if (ret == 0) {
|
|
cout << "Bitmap::savePNG(): Could not save PNG file \"" << path << "%s\"" << endl;
|
|
}
|
|
|
|
delete[] rgb8;
|
|
}
|
|
|
|
NORI_NAMESPACE_END
|