mtsgui: ability to view multi-channel EXRs
parent
461827ec11
commit
9f9df009ef
|
@ -297,6 +297,22 @@ public:
|
|||
EDivision
|
||||
};
|
||||
|
||||
/**
|
||||
* \brief Describes a sub-layer of a multilayer bitmap (e.g. OpenEXR)
|
||||
*
|
||||
* A layer is defined as a named collection of bitmap channels and a pixel format
|
||||
*
|
||||
* This data structure is used by \ref Bitmap::getLayers()
|
||||
*/
|
||||
struct Layer {
|
||||
/// Descriptive name of the bitmap layer
|
||||
std::string name;
|
||||
/// Pixel format that should be associated with underlying combination of bitmap channels
|
||||
EPixelFormat format;
|
||||
/// References to bitmap channels
|
||||
std::vector<int> channels;
|
||||
};
|
||||
|
||||
/**
|
||||
* \brief Create a bitmap of the specified type and allocate
|
||||
* the necessary amount of memory
|
||||
|
@ -813,9 +829,26 @@ public:
|
|||
*/
|
||||
ref<Bitmap> extractChannels(Bitmap::EPixelFormat fmt, const std::vector<int> &channels) const;
|
||||
|
||||
/**
|
||||
* \brief Extracts layer information from the bitmap
|
||||
*
|
||||
* This is a convenience function which analyzes the bitmap's
|
||||
* channel names and extracts groups matching a name
|
||||
* convention specified by the OpenEXR standard.
|
||||
*
|
||||
* A series of channel names referred to as
|
||||
* 'diffuse.R', 'diffuse.B', and 'diffuse.G'
|
||||
* will be identified as a single \ref Bitmap::ERGB "layer".
|
||||
* This works for RGB[A]/XYZ[A], and Luminance[A] images
|
||||
*/
|
||||
std::vector<Layer> getLayers() const;
|
||||
|
||||
/**
|
||||
* \brief Split an multi-channel image buffer (e.g. from an OpenEXR image
|
||||
* with lots of AOVs) into group of RGB[A]/XYZ[A]/Luminance images
|
||||
* with lots of AOVs) into its constituent layers
|
||||
*
|
||||
* This operation internally calls \ref getLayers() to extract bitmap
|
||||
* layers followed by one or more calls to \ref extractChannels()
|
||||
*/
|
||||
std::map<std::string, Bitmap *> split() const;
|
||||
|
||||
|
|
|
@ -1875,32 +1875,34 @@ void Bitmap::tonemapReinhard(Float &logAvgLuminance, Float &maxLuminance, Float
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
std::map<std::string, Bitmap *> Bitmap::split() const {
|
||||
std::vector<Bitmap::Layer> Bitmap::getLayers() const {
|
||||
typedef std::map<std::string, int> ChannelMap;
|
||||
std::map<std::string, Bitmap *> result;
|
||||
if (m_channelNames.empty())
|
||||
Log(EError, "Bitmap::split(): color channel names not available!");
|
||||
Log(EError, "Bitmap::getLayers(): required color channel names were not available!");
|
||||
|
||||
ChannelMap channels;
|
||||
for (size_t i=0; i<m_channelNames.size(); ++i)
|
||||
channels[boost::to_lower_copy(m_channelNames[i])] = (int) i;
|
||||
|
||||
std::vector<Layer> layers;
|
||||
for (size_t i=0; i<m_channelNames.size(); ++i) {
|
||||
std::string name = boost::to_lower_copy(m_channelNames[i]);
|
||||
if (channels.find(name) == channels.end())
|
||||
continue;
|
||||
std::string prefix = "";
|
||||
std::string prefix = name;
|
||||
char postfix = '\0';
|
||||
Layer layer;
|
||||
|
||||
if (!name.empty()) {
|
||||
prefix = name.substr(0, name.length()-1);
|
||||
postfix = name[name.length()-1];
|
||||
layer.name = m_channelNames[i];
|
||||
if (name.length() == 1) {
|
||||
prefix = layer.name = "";
|
||||
postfix = name[name.length() - 1];
|
||||
} if (name.length() >= 3 && name[name.length()-2] == '.') {
|
||||
prefix = name.substr(0, name.length() - 1);
|
||||
layer.name = layer.name.substr(0, layer.name.length() - 2);
|
||||
postfix = name[name.length() - 1];
|
||||
}
|
||||
|
||||
std::vector<int> extractChannels;
|
||||
EPixelFormat extractFormat;
|
||||
|
||||
ChannelMap::iterator
|
||||
itR = channels.find(prefix + "r"),
|
||||
itG = channels.find(prefix + "g"),
|
||||
|
@ -1915,68 +1917,75 @@ std::map<std::string, Bitmap *> Bitmap::split() const {
|
|||
bool maybeY = postfix == 'y' || postfix == 'a';
|
||||
|
||||
if (maybeRGB && itR != channels.end() && itG != channels.end() && itB != channels.end()) {
|
||||
extractFormat = ERGB;
|
||||
extractChannels.push_back(itR->second);
|
||||
extractChannels.push_back(itG->second);
|
||||
extractChannels.push_back(itB->second);
|
||||
layer.format = ERGB;
|
||||
layer.channels.push_back(itR->second);
|
||||
layer.channels.push_back(itG->second);
|
||||
layer.channels.push_back(itB->second);
|
||||
if (itA != channels.end()) {
|
||||
extractFormat = ERGBA;
|
||||
extractChannels.push_back(itA->second);
|
||||
layer.format = ERGBA;
|
||||
layer.channels.push_back(itA->second);
|
||||
channels.erase(prefix + "a");
|
||||
}
|
||||
channels.erase(prefix + "r");
|
||||
channels.erase(prefix + "g");
|
||||
channels.erase(prefix + "b");
|
||||
} else if (maybeXYZ && itX != channels.end() && itY != channels.end() && itZ != channels.end()) {
|
||||
extractFormat = EXYZ;
|
||||
extractChannels.push_back(itX->second);
|
||||
extractChannels.push_back(itY->second);
|
||||
extractChannels.push_back(itZ->second);
|
||||
layer.format = EXYZ;
|
||||
layer.channels.push_back(itX->second);
|
||||
layer.channels.push_back(itY->second);
|
||||
layer.channels.push_back(itZ->second);
|
||||
if (itA != channels.end()) {
|
||||
extractFormat = EXYZA;
|
||||
extractChannels.push_back(itA->second);
|
||||
layer.format = EXYZA;
|
||||
layer.channels.push_back(itA->second);
|
||||
channels.erase(prefix + "a");
|
||||
}
|
||||
channels.erase(prefix + "x");
|
||||
channels.erase(prefix + "y");
|
||||
channels.erase(prefix + "z");
|
||||
} else if (maybeY && itY != channels.end()) {
|
||||
extractFormat = ELuminance;
|
||||
extractChannels.push_back(itY->second);
|
||||
layer.format = ELuminance;
|
||||
layer.channels.push_back(itY->second);
|
||||
if (itA != channels.end()) {
|
||||
extractFormat = ELuminanceAlpha;
|
||||
extractChannels.push_back(itA->second);
|
||||
layer.format = ELuminanceAlpha;
|
||||
layer.channels.push_back(itA->second);
|
||||
channels.erase(prefix + "a");
|
||||
}
|
||||
channels.erase(prefix + "y");
|
||||
} else {
|
||||
extractFormat = ELuminance;
|
||||
extractChannels.push_back((int) i);
|
||||
if (layer.name.empty())
|
||||
layer.name = m_channelNames[i];
|
||||
layer.format = ELuminance;
|
||||
layer.channels.push_back((int) i);
|
||||
channels.erase(name);
|
||||
}
|
||||
|
||||
layers.push_back(layer);
|
||||
}
|
||||
return layers;
|
||||
}
|
||||
|
||||
std::map<std::string, Bitmap *> Bitmap::split() const {
|
||||
std::map<std::string, Bitmap *> result;
|
||||
|
||||
std::vector<Layer> layers = getLayers();
|
||||
for (size_t i=0; i<layers.size(); ++i) {
|
||||
const Layer &layer = layers[i];
|
||||
std::vector<std::string> channelNames;
|
||||
for (size_t j=0; j<extractChannels.size(); ++j)
|
||||
channelNames.push_back(m_channelNames[extractChannels[j]]);
|
||||
for (size_t j=0; j<layer.channels.size(); ++j)
|
||||
channelNames.push_back(m_channelNames[layer.channels[j]]);
|
||||
|
||||
Bitmap *bitmap = NULL;
|
||||
{
|
||||
ref<Bitmap> _bitmap = this->extractChannels(extractFormat, extractChannels);
|
||||
ref<Bitmap> _bitmap = this->extractChannels(layer.format, layer.channels);
|
||||
bitmap = _bitmap.get();
|
||||
bitmap->incRef();
|
||||
}
|
||||
bitmap->decRef(false);
|
||||
bitmap->setChannelNames(channelNames);
|
||||
|
||||
prefix = m_channelNames[i];
|
||||
if (!prefix.empty())
|
||||
prefix = prefix.substr(0, prefix.length()-1);
|
||||
if (!prefix.empty() && prefix[prefix.length()-1] == '.')
|
||||
prefix = prefix.substr(0, prefix.length()-1);
|
||||
|
||||
if (result.find(prefix) != result.end())
|
||||
Log(EError, "Internal error -- encountered two sub-images with the same prefix");
|
||||
result[prefix] = bitmap;
|
||||
if (result.find(layer.name) != result.end())
|
||||
Log(EError, "Internal error -- encountered two layers with the same name \"%s\"", layer.name.c_str());
|
||||
result[layer.name] = bitmap;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
|
|
@ -771,7 +771,8 @@ void GLRenderer::drawText(const Point2i &_pos,
|
|||
glLoadIdentity();
|
||||
font->getTexture()->bind();
|
||||
glEnable(GL_BLEND);
|
||||
glBlendFunc(GL_ONE_MINUS_DST_COLOR, GL_ONE_MINUS_SRC_COLOR);
|
||||
glBlendFunc(GL_SRC_ALPHA, GL_ONE);
|
||||
// glBlendFunc(GL_ONE_MINUS_DST_COLOR, GL_ONE_MINUS_SRC_COLOR);
|
||||
Point2i pos(_pos);
|
||||
int initial = pos.x;
|
||||
|
||||
|
|
|
@ -1110,7 +1110,7 @@ void TriMesh::writePLY(const fs::path &path) const {
|
|||
*ptr += (uint8_t) std::max(0.0f, std::min(255.0f, (float) m_colors[i][2] * 255.0f + 0.5f));
|
||||
}
|
||||
}
|
||||
Assert(ptr-vertexStorage == vertexStorageSize);
|
||||
Assert((size_t) (ptr-vertexStorage) == vertexStorageSize);
|
||||
os.write((const char *) vertexStorage, vertexStorageSize);
|
||||
delete[] vertexStorage;
|
||||
|
||||
|
@ -1122,7 +1122,7 @@ void TriMesh::writePLY(const fs::path &path) const {
|
|||
memcpy(ptr, &m_triangles[i], sizeof(Triangle));
|
||||
ptr += sizeof(Triangle);
|
||||
}
|
||||
Assert(ptr-faceStorage == faceStorageSize);
|
||||
Assert((size_t) (ptr-faceStorage) == faceStorageSize);
|
||||
os.write((const char *) faceStorage, faceStorageSize);
|
||||
delete[] faceStorage;
|
||||
|
||||
|
|
|
@ -184,7 +184,9 @@ struct SceneContext {
|
|||
float progress;
|
||||
QString eta, progressName;
|
||||
ref<Bitmap> framebuffer;
|
||||
std::vector<std::pair<std::string, Bitmap*> > layers;
|
||||
std::set<VisualWorkUnit, block_comparator> workUnits;
|
||||
int currentLayer;
|
||||
EMode mode, cancelMode;
|
||||
Float gamma, exposure, clamping;
|
||||
bool srgb;
|
||||
|
@ -209,6 +211,7 @@ struct SceneContext {
|
|||
|
||||
SceneContext() : scene(NULL), sceneResID(-1),
|
||||
renderJob(NULL), wasRendering(false),
|
||||
currentLayer(0),
|
||||
selectionMode(ENothing),
|
||||
selectedShape(NULL) { }
|
||||
|
||||
|
|
|
@ -220,6 +220,14 @@ void GLWidget::setScene(SceneContext *context) {
|
|||
m_movementTimer->stop();
|
||||
m_context = context;
|
||||
|
||||
if (context && context->layers.size() > 0) {
|
||||
m_statusMessage =
|
||||
formatString("Showing layer \"%s\" (%i/%i); use '[' and ']' to switch.",
|
||||
m_context->layers[m_context->currentLayer].first.c_str(),
|
||||
m_context->currentLayer+1, m_context->layers.size());
|
||||
m_statusTimer->reset();
|
||||
}
|
||||
|
||||
if (context && context->scene == NULL)
|
||||
context = NULL;
|
||||
|
||||
|
@ -503,7 +511,9 @@ void GLWidget::keyPressEvent(QKeyEvent *event) {
|
|||
emit stopRendering();
|
||||
}
|
||||
|
||||
if (event->isAutoRepeat() || !m_context)
|
||||
if (!m_context ||
|
||||
(event->isAutoRepeat() && event->key() != Qt::Key_BracketLeft &&
|
||||
event->key() != Qt::Key_BracketRight))
|
||||
return;
|
||||
|
||||
switch (event->key()) {
|
||||
|
@ -526,7 +536,15 @@ void GLWidget::keyPressEvent(QKeyEvent *event) {
|
|||
case Qt::Key_Down:
|
||||
m_downKeyDown = true; break;
|
||||
case Qt::Key_BracketLeft:
|
||||
if (m_context->showKDTree) {
|
||||
if (m_context->layers.size() > 0) {
|
||||
m_context->currentLayer = std::max(0, m_context->currentLayer-1);
|
||||
m_context->framebuffer = m_context->layers[m_context->currentLayer].second->convert(Bitmap::ERGBA, Bitmap::EFloat32);
|
||||
m_statusMessage =
|
||||
formatString("Showing layer \"%s\" (%i/%i); use '[' and ']' to switch.",
|
||||
m_context->layers[m_context->currentLayer].first.c_str(),
|
||||
m_context->currentLayer+1, m_context->layers.size());
|
||||
m_statusTimer->reset();
|
||||
} else if (m_context->showKDTree) {
|
||||
m_context->shownKDTreeLevel
|
||||
= std::max(0, m_context->shownKDTreeLevel - 1);
|
||||
updateGL();
|
||||
|
@ -534,7 +552,15 @@ void GLWidget::keyPressEvent(QKeyEvent *event) {
|
|||
}
|
||||
break;
|
||||
case Qt::Key_BracketRight:
|
||||
if (m_context->showKDTree) {
|
||||
if (m_context->layers.size() > 0) {
|
||||
m_context->currentLayer = std::min((int) m_context->layers.size() - 1, m_context->currentLayer+1);
|
||||
m_context->framebuffer = m_context->layers[m_context->currentLayer].second->convert(Bitmap::ERGBA, Bitmap::EFloat32);
|
||||
m_statusMessage =
|
||||
formatString("Showing layer \"%s\" (%i/%i); use '[' and ']' to switch.",
|
||||
m_context->layers[m_context->currentLayer].first.c_str(),
|
||||
m_context->currentLayer+1, m_context->layers.size());
|
||||
m_statusTimer->reset();
|
||||
} else if (m_context->showKDTree) {
|
||||
m_context->shownKDTreeLevel++;
|
||||
updateGL();
|
||||
return;
|
||||
|
@ -1215,11 +1241,17 @@ void GLWidget::paintGL() {
|
|||
if (m_context->mode == EPreview) {
|
||||
m_preview->releaseBuffer(entry);
|
||||
if (m_context->showKDTree) {
|
||||
std::string message = "kd-tree visualization mode\nPress '[' "
|
||||
"and ']' to change the shown level";
|
||||
Vector2i size = m_font->getSize(message);
|
||||
int pos = m_statusMessage.empty() ? 10 : 30;
|
||||
m_renderer->setBlendMode(Renderer::EBlendAlpha);
|
||||
m_renderer->setColor(Spectrum(0.0f), 0.5f);
|
||||
m_renderer->drawFilledRectangle(Point2i(5, pos-2), Point2i(15, pos+5)+size);
|
||||
m_renderer->setBlendMode(Renderer::EBlendNone);
|
||||
m_renderer->setColor(Spectrum(1.0f));
|
||||
m_renderer->drawText(Point2i(10, pos), m_font,
|
||||
formatString("kd-tree visualization mode\nPress '[' "
|
||||
"and ']' to change the shown level"));
|
||||
m_renderer->setColor(Spectrum(1.0f));
|
||||
m_renderer->drawText(Point2i(10, pos), m_font, message);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1244,7 +1276,15 @@ void GLWidget::paintGL() {
|
|||
m_statusMessage = "Camera type is incompatible with the OpenGL preview!";
|
||||
}
|
||||
|
||||
if (!m_statusMessage.empty() && m_context->mode == EPreview) {
|
||||
if (!m_statusMessage.empty() && (m_context->mode == EPreview
|
||||
|| (m_context->mode == ERender && m_context->scene == NULL))) {
|
||||
m_renderer->setDepthTest(false);
|
||||
Vector2i size = m_font->getSize(m_statusMessage);
|
||||
m_renderer->setBlendMode(Renderer::EBlendAlpha);
|
||||
m_renderer->setColor(Spectrum(0.0f), 0.5f);
|
||||
m_renderer->drawFilledRectangle(Point2i(5, 7), Point2i(15, 15)+size);
|
||||
m_renderer->setBlendMode(Renderer::EBlendNone);
|
||||
m_renderer->setColor(Spectrum(1.0f));
|
||||
m_renderer->drawText(Point2i(10, 10), m_font, m_statusMessage);
|
||||
if (m_statusTimer->getMilliseconds() > 2000)
|
||||
m_statusMessage = "";
|
||||
|
|
|
@ -398,7 +398,7 @@ bool MainWindow::initWorkersProcessArgv() {
|
|||
scheduler->registerWorker(new LocalWorker(-1, formatString("wrk%i", 0), m_workerPriority));
|
||||
}
|
||||
|
||||
for (int i=0; i<toBeLoaded.size(); ++i)
|
||||
for (size_t i=0; i<toBeLoaded.size(); ++i)
|
||||
loadFile(toBeLoaded[i].first, toBeLoaded[i].second);
|
||||
|
||||
scheduler->start();
|
||||
|
@ -2157,6 +2157,10 @@ SceneContext::SceneContext(SceneContext *ctx) {
|
|||
selectionMode = ctx->selectionMode;
|
||||
originalSize = ctx->originalSize;
|
||||
doc = ctx->doc.cloneNode(true).toDocument();
|
||||
layers = ctx->layers;
|
||||
for (size_t i=0; i<layers.size(); ++i)
|
||||
layers[i].second->incRef();
|
||||
currentLayer = ctx->currentLayer;
|
||||
}
|
||||
|
||||
SceneContext::~SceneContext() {
|
||||
|
@ -2166,6 +2170,8 @@ SceneContext::~SceneContext() {
|
|||
previewBuffer.buffer->decRef();
|
||||
if (previewBuffer.sync)
|
||||
previewBuffer.sync->decRef();
|
||||
for (size_t i=0; i<layers.size(); ++i)
|
||||
layers[i].second->decRef();
|
||||
}
|
||||
|
||||
int SceneContext::detectPathLength() const {
|
||||
|
|
|
@ -65,12 +65,25 @@ void SceneLoader::run() {
|
|||
m_result->toneMappingMethod = (EToneMappingMethod) settings.value("preview_toneMappingMethod", EGamma).toInt();
|
||||
m_result->diffuseSources = settings.value("preview_diffuseSources", true).toBool();
|
||||
m_result->diffuseReceivers = settings.value("preview_diffuseReceivers", false).toBool();
|
||||
m_result->currentLayer = 0;
|
||||
|
||||
if (suffix == "exr" || suffix == "png" || suffix == "jpg" || suffix == "jpeg" ||
|
||||
suffix == "hdr" || suffix == "rgbe" || suffix == "pfm" || suffix == "ppm") {
|
||||
/* This is an image, not a scene */
|
||||
ref<FileStream> fs = new FileStream(toFsPath(m_filename), FileStream::EReadOnly);
|
||||
ref<Bitmap> bitmap = new Bitmap(Bitmap::EAuto, fs);
|
||||
ref<Bitmap> bitmap = new Bitmap(Bitmap::EAuto, fs, (suffix == "exr") ? "*" : "");
|
||||
|
||||
if (suffix == "exr" && bitmap->getLayers().size() > 1) {
|
||||
std::map<std::string, Bitmap*> bitmaps = bitmap->split();
|
||||
for (std::map<std::string, Bitmap *>::iterator it = bitmaps.begin(); it != bitmaps.end(); ++it) {
|
||||
std::string name = it->first;
|
||||
Bitmap *layer = it->second;
|
||||
layer->incRef();
|
||||
m_result->layers.push_back(std::make_pair(name, layer));
|
||||
}
|
||||
bitmap = m_result->layers[m_result->currentLayer].second;
|
||||
}
|
||||
|
||||
bitmap = bitmap->convert(Bitmap::ERGBA, Bitmap::EFloat32);
|
||||
|
||||
m_result->mode = ERender;
|
||||
|
|
Loading…
Reference in New Issue