[prev in list] [next in list] [prev in thread] [next in thread]
List: kde-commits
Subject: [calligra/calligra/2.9] krita/plugins/formats/exr: BUG:352734 Support top-level RGB channels
From: Boudewijn Rempt <boud () valdyas ! org>
Date: 2015-09-21 10:03:13
Message-ID: E1ZdxwH-0008US-KH () scm ! kde ! org
[Download RAW message or body]
Git commit 337183f80217471dd2d6056d4597e57c4bc1f956 by Boudewijn Rempt.
Committed on 21/09/2015 at 10:00.
Pushed by rempt into branch 'calligra/2.9'.
BUG:352734 Support top-level RGB channels
Some OpenEXR images have both layers and top-level channels. If
there are top-level channels named R, G, B, A, we should read them
and construct a synthetic layer out of them.
M +115 -100 krita/plugins/formats/exr/exr_converter.cc
M +2 -0 krita/plugins/formats/exr/exr_import.cc
http://commits.kde.org/calligra/337183f80217471dd2d6056d4597e57c4bc1f956
diff --git a/krita/plugins/formats/exr/exr_converter.cc \
b/krita/plugins/formats/exr/exr_converter.cc index 0874e65..a38e7b7 100644
--- a/krita/plugins/formats/exr/exr_converter.cc
+++ b/krita/plugins/formats/exr/exr_converter.cc
@@ -89,16 +89,21 @@ enum ImageType {
};
struct ExrPaintLayerInfo : public ExrLayerInfoBase {
- ExrPaintLayerInfo() : imageType(IT_UNKNOWN) {
+ ExrPaintLayerInfo()
+ : imageType(IT_UNKNOWN)
+ {
}
+
ImageType imageType;
QMap< QString, QString> channelMap; ///< first is either R, G, B or A second is \
the EXR channel name +
struct Remap {
Remap(const QString& _original, const QString& _current) : \
original(_original), current(_current) { }
QString original;
QString current;
};
+
QList< Remap > remappedChannels; ///< this is used to store in the metadata the \
mapping between exr channel name, and channels used in Krita void \
updateImageType(ImageType channelType); };
@@ -107,7 +112,8 @@ void ExrPaintLayerInfo::updateImageType(ImageType channelType)
{
if (imageType == IT_UNKNOWN) {
imageType = channelType;
- } else if (imageType != channelType) {
+ }
+ else if (imageType != channelType) {
imageType = IT_UNSUPPORTED;
}
}
@@ -121,7 +127,7 @@ struct ExrPaintLayerSaveInfo {
struct exrConverter::Private {
Private() : doc(0), warnedAboutChangedAlpha(false),
- showNotifications(false) {}
+ showNotifications(false) {}
KisImageSP image;
KisDocument *doc;
@@ -226,9 +232,9 @@ struct RgbPixelWrapper
const T alpha = pixel.a;
return alpha >= alphaNoiseThreshold<T>() ||
- (pixel.r * alpha == mult.r &&
- pixel.g * alpha == mult.g &&
- pixel.b * alpha == mult.b);
+ (pixel.r * alpha == mult.r &&
+ pixel.g * alpha == mult.g &&
+ pixel.b * alpha == mult.b);
}
inline void setUnmultiplied(const Rgba<T> &mult, qreal newAlpha) {
@@ -262,7 +268,7 @@ struct GrayPixelWrapper
const T alpha = pixel.alpha;
return alpha >= alphaNoiseThreshold<T>() ||
- pixel.gray * alpha == mult.gray;
+ pixel.gray * alpha == mult.gray;
}
inline void setUnmultiplied(const pixel_type &mult, qreal newAlpha) {
@@ -307,20 +313,20 @@ void exrConverter::Private::unmultiplyAlpha(typename \
WrapperType::pixel_type *pi
*pixel = dstPixel.pixel;
if (alphaWasModified &&
- !this->warnedAboutChangedAlpha) {
+ !this->warnedAboutChangedAlpha) {
QString msg =
- i18nc("@info",
- "The image contains pixels with zero alpha channel and \
non-zero "
- "color channels. Krita will have to modify those pixels to \
have "
- "at least some alpha. The initial values will \
<emphasis>not</emphasis> "
- "be reverted on saving the image back."
- "<nl/><nl/>"
- "This will hardly make any visual difference just keep it in \
mind."
- "<nl/><nl/>"
- "<note>Modified alpha will have a range from <numid>%1</numid> \
to <numid>%2</numid></note>",
- alphaEpsilon<channel_type>(),
- alphaNoiseThreshold<channel_type>());
+ i18nc("@info",
+ "The image contains pixels with zero alpha channel and \
non-zero " + "color channels. Krita will have to modify \
those pixels to have " + "at least some alpha. The initial \
values will <emphasis>not</emphasis> " + "be reverted on \
saving the image back." + "<nl/><nl/>"
+ "This will hardly make any visual difference just keep it \
in mind." + "<nl/><nl/>"
+ "<note>Modified alpha will have a range from \
<numid>%1</numid> to <numid>%2</numid></note>", + \
alphaEpsilon<channel_type>(), + \
alphaNoiseThreshold<channel_type>());
if (this->showNotifications) {
QMessageBox::information(0, i18nc("@title:window", "EXR image will \
be modified"), msg); @@ -365,22 +371,22 @@ void \
exrConverter::Private::decodeData4(Imf::InputFile& file, ExrPaintLayerInfo& \
Imf::FrameBuffer frameBuffer;
Rgba* frameBufferData = (pixels.data()) - xstart - (ystart + y) * width;
frameBuffer.insert(info.channelMap["R"].toLatin1().constData(),
- Imf::Slice(ptype, (char *) &frameBufferData->r,
- sizeof(Rgba) * 1,
- sizeof(Rgba) * width));
+ Imf::Slice(ptype, (char *) &frameBufferData->r,
+ sizeof(Rgba) * 1,
+ sizeof(Rgba) * width));
frameBuffer.insert(info.channelMap["G"].toLatin1().constData(),
- Imf::Slice(ptype, (char *) &frameBufferData->g,
- sizeof(Rgba) * 1,
- sizeof(Rgba) * width));
+ Imf::Slice(ptype, (char *) &frameBufferData->g,
+ sizeof(Rgba) * 1,
+ sizeof(Rgba) * width));
frameBuffer.insert(info.channelMap["B"].toLatin1().constData(),
- Imf::Slice(ptype, (char *) &frameBufferData->b,
- sizeof(Rgba) * 1,
- sizeof(Rgba) * width));
+ Imf::Slice(ptype, (char *) &frameBufferData->b,
+ sizeof(Rgba) * 1,
+ sizeof(Rgba) * width));
if (hasAlpha) {
frameBuffer.insert(info.channelMap["A"].toLatin1().constData(),
- Imf::Slice(ptype, (char *) &frameBufferData->a,
- sizeof(Rgba) * 1,
- sizeof(Rgba) * width));
+ Imf::Slice(ptype, (char *) &frameBufferData->a,
+ sizeof(Rgba) * 1,
+ sizeof(Rgba) * width));
}
file.setFrameBuffer(frameBuffer);
@@ -418,7 +424,7 @@ void exrConverter::Private::decodeData1(Imf::InputFile& file, \
ExrPaintLayerInfo& typedef typename GrayPixelWrapper<_T_>::pixel_type pixel_type;
KIS_ASSERT_RECOVER_RETURN(
- layer->paintDevice()->colorSpace()->colorModelId() == GrayAColorModelID);
+ layer->paintDevice()->colorSpace()->colorModelId() == \
GrayAColorModelID);
QVector<pixel_type> pixels(width);
@@ -433,15 +439,15 @@ void exrConverter::Private::decodeData1(Imf::InputFile& file, \
ExrPaintLayerInfo& Imf::FrameBuffer frameBuffer;
pixel_type* frameBufferData = (pixels.data()) - xstart - (ystart + y) * \
width; frameBuffer.insert(info.channelMap["G"].toLatin1().constData(),
- Imf::Slice(ptype, (char *) &frameBufferData->gray,
- sizeof(pixel_type) * 1,
- sizeof(pixel_type) * width));
+ Imf::Slice(ptype, (char *) &frameBufferData->gray,
+ sizeof(pixel_type) * 1,
+ sizeof(pixel_type) * width));
if (hasAlpha) {
frameBuffer.insert(info.channelMap["A"].toLatin1().constData(),
- Imf::Slice(ptype, (char *) &frameBufferData->alpha,
- sizeof(pixel_type) * 1,
- sizeof(pixel_type) * width));
+ Imf::Slice(ptype, (char *) &frameBufferData->alpha,
+ sizeof(pixel_type) * 1,
+ sizeof(pixel_type) * width));
}
file.setFrameBuffer(frameBuffer);
@@ -497,7 +503,7 @@ ExrGroupLayerInfo* searchGroup(QList<ExrGroupLayerInfo>* groups, \
QStringList lis QDomDocument exrConverter::Private::loadExtraLayersInfo(const \
Imf::Header &header) {
const Imf::StringAttribute *layersInfoAttribute =
- header.findTypedAttribute<Imf::StringAttribute>(EXR_KRITA_LAYERS);
+ header.findTypedAttribute<Imf::StringAttribute>(EXR_KRITA_LAYERS);
if (!layersInfoAttribute) return QDomDocument();
@@ -525,7 +531,7 @@ bool exrConverter::Private::checkExtraLayersInfoConsistent(const \
QDomDocument &d el = el.nextSiblingElement();
}
- bool result = extraInfoLayers == exrLayerNames;
+ bool result = (extraInfoLayers == exrLayerNames);
if (!result) {
qDebug() << "WARINING: Krita EXR extra layers info is inconsistent!";
@@ -549,6 +555,7 @@ bool exrConverter::Private::checkExtraLayersInfoConsistent(const \
QDomDocument &d KisImageBuilder_Result exrConverter::decode(const KUrl& uri)
{
dbgFile << "Load exr: " << uri << " " << QFile::encodeName(uri.toLocalFile());
+
Imf::InputFile file(QFile::encodeName(uri.toLocalFile()));
Imath::Box2i dw = file.header().dataWindow();
@@ -559,7 +566,7 @@ KisImageBuilder_Result exrConverter::decode(const KUrl& uri)
// Display the attributes of a file
for (Imf::Header::ConstIterator it = file.header().begin();
- it != file.header().end(); ++it) {
+ it != file.header().end(); ++it) {
dbgFile << "Attribute: " << it.name() << " type: " << \
it.attribute().typeName(); }
@@ -578,70 +585,78 @@ KisImageBuilder_Result exrConverter::decode(const KUrl& uri)
channels.layers(layerNames);
if (!extraLayersInfo.isNull() &&
- !m_d->checkExtraLayersInfoConsistent(extraLayersInfo, layerNames)) {
+ !m_d->checkExtraLayersInfoConsistent(extraLayersInfo, layerNames)) {
// it is inconsistent anyway
extraLayersInfo = QDomDocument();
}
- // Test if it is a multilayer EXR or singlelayer
- if (layerNames.empty()) {
- dbgFile << "Single layer:";
- ExrPaintLayerInfo info;
- info.name = i18n("HDR Layer");
- for (Imf::ChannelList::ConstIterator i = channels.begin(); i != \
channels.end(); ++i) {
- const Imf::Channel &channel = i.channel();
- dbgFile << "Channel name = " << i.name() << " type = " << channel.type;
+ // Check if there are A, R, G, B channels
- info.updateImageType(imfTypeToKisType(channel.type));
+ dbgFile << "Checking for ARGB channels, they can occur in single-layer _or_ \
multi-layer images:"; + ExrPaintLayerInfo info;
+ bool topLevelRGBFound = false;
+ info.name = i18n("HDR Layer");
- QString qname = i.name();
- if (qname != "A" && qname != "R" && qname != "G" && qname != "B") {
- dbgFile << "Unknow: " << i.name();
- info.imageType = IT_UNSUPPORTED;
- } else {
- info.channelMap[qname] = qname;
- }
+ for (Imf::ChannelList::ConstIterator i = channels.begin(); i != channels.end(); \
++i) { + const Imf::Channel &channel = i.channel();
+ dbgFile << "Channel name = " << i.name() << " type = " << channel.type;
+
+ QString qname = i.name();
+ QStringList topLevelChannelNames = QStringList() << "A" << "R" << "G" << \
"B"; + if (topLevelChannelNames.contains(qname)) {
+ topLevelRGBFound = true;
+ dbgFile << "Found top-level channel" << qname;
+ info.channelMap[qname] = qname;
+ info.updateImageType(imfTypeToKisType(channel.type));
+ }
+ else if (qname.startsWith('.') || !qname.contains('.')) {
+ warnFile << "Found a top-level channel that is not part of the rendered \
image" << qname << ". Krita will not load this channel."; }
+ }
+ if (topLevelRGBFound) {
+ dbgFile << "Toplevel layer" << info.name << ":Image type:" << imageType << \
"Layer type" << info.imageType; informationObjects.push_back(info);
imageType = info.imageType;
- } else {
- dbgFile << "Multi layers:";
- for (std::set<std::string>::const_iterator i = layerNames.begin();
- i != layerNames.end(); ++i) {
- ExrPaintLayerInfo info;
- dbgFile << "layer name = " << i->c_str();
- info.name = i->c_str();
- Imf::ChannelList::ConstIterator layerBegin, layerEnd;
- channels.channelsInLayer(*i, layerBegin, layerEnd);
- for (Imf::ChannelList::ConstIterator j = layerBegin;
- j != layerEnd; ++j) {
- const Imf::Channel &channel = j.channel();
- dbgFile << "\tchannel " << j.name() << " type = " << channel.type;
-
- info.updateImageType(imfTypeToKisType(channel.type));
-
- QString qname = j.name();
- QStringList list = qname.split('.');
- QString layersuffix = list.last();
-
- if (list.size() > 1) {
- info.name = list[list.size()-2];
- info.parent = searchGroup(&groups, list, 0, list.size() - 3);
- }
+ }
+
+ dbgFile << "Extra layers:" << layerNames.size();
- info.channelMap[layersuffix] = qname;
+ for (std::set<std::string>::const_iterator i = layerNames.begin();
+ i != layerNames.end(); ++i) {
+ dbgFile << "layer name = " << i->c_str();
+ info.name = i->c_str();
+ Imf::ChannelList::ConstIterator layerBegin, layerEnd;
+ channels.channelsInLayer(*i, layerBegin, layerEnd);
+ for (Imf::ChannelList::ConstIterator j = layerBegin;
+ j != layerEnd; ++j) {
+ const Imf::Channel &channel = j.channel();
+ dbgFile << "\tchannel " << j.name() << " type = " << channel.type;
+
+ info.updateImageType(imfTypeToKisType(channel.type));
+
+ QString qname = j.name();
+ QStringList list = qname.split('.');
+ QString layersuffix = list.last();
+
+ if (list.size() > 1) {
+ info.name = list[list.size()-2];
+ info.parent = searchGroup(&groups, list, 0, list.size() - 3);
}
- if (info.imageType != IT_UNKNOWN && info.imageType != IT_UNSUPPORTED) {
- informationObjects.push_back(info);
- if (imageType < info.imageType) {
- imageType = info.imageType;
- }
+
+ info.channelMap[layersuffix] = qname;
+ }
+ if (info.imageType != IT_UNKNOWN && info.imageType != IT_UNSUPPORTED) {
+ informationObjects.push_back(info);
+ if (imageType < info.imageType) {
+ imageType = info.imageType;
}
}
}
- dbgFile << "File has " << informationObjects.size() << " layers";
+
+ dbgFile << "File has" << informationObjects.size() << "layer(s)";
+
// Set the colorspaces
for (int i = 0; i < informationObjects.size(); ++i) {
ExrPaintLayerInfo& info = informationObjects[i];
@@ -672,7 +687,7 @@ KisImageBuilder_Result exrConverter::decode(const KUrl& uri)
}
info.remappedChannels.push_back(
- ExrPaintLayerInfo::Remap(failingChannelKey, "G"));
+ ExrPaintLayerInfo::Remap(failingChannelKey, "G"));
QString failingChannelValue = info.channelMap[failingChannelKey];
info.channelMap.remove(failingChannelKey);
@@ -919,7 +934,7 @@ void EncoderImpl<_T_, size, alphaPos>::encodeData(int line)
Encoder* encoder(Imf::OutputFile& file, const ExrPaintLayerSaveInfo& info, int \
width) {
-// bool hasAlpha = info.layer->colorSpace()->channelCount() != \
info.layer->colorSpace()->colorChannelCount(); + // bool hasAlpha = \
info.layer->colorSpace()->channelCount() != \
info.layer->colorSpace()->colorChannelCount(); switch \
(info.layer->colorSpace()->channelCount()) { case 1: {
if (info.layer->colorSpace()->colorDepthId() == Float16BitsColorDepthID) {
@@ -1009,9 +1024,9 @@ KisImageBuilder_Result exrConverter::buildFile(const KUrl& uri, \
KisPaintLayerSP
if(pixelType >= Imf::NUM_PIXELTYPES)
{
- return KisImageBuilder_RESULT_UNSUPPORTED_COLORSPACE;
+ return KisImageBuilder_RESULT_UNSUPPORTED_COLORSPACE;
}
-
+
header.channels().insert("R", Imf::Channel(pixelType));
header.channels().insert("G", Imf::Channel(pixelType));
@@ -1070,9 +1085,9 @@ void \
exrConverter::Private::makeLayerNamesUnique(QList<ExrPaintLayerSaveInfo>& i
for (; it != end; ++it) {
QString newName =
- QString("%1_%2.")
- .arg(strippedName)
- .arg(nameCounter++);
+ QString("%1_%2.")
+ .arg(strippedName)
+ .arg(nameCounter++);
it.value()->name = newName;
@@ -1167,10 +1182,10 @@ void exrConverter::Private::reportLayersNotSaved(const \
QSet<KisNodeSP> &layersNo }
QString msg =
- i18nc("@info",
- "<para>The following layers have a type that is not supported by EXR \
format:</para>"
- "<para><list>%1</list></para>"
- "<para><warning>these layers will NOT be saved to the final EXR \
file</warning></para>", layersList); + i18nc("@info",
+ "<para>The following layers have a type that is not supported by \
EXR format:</para>" + "<para><list>%1</list></para>"
+ "<para><warning>these layers will NOT be saved to the final EXR \
file</warning></para>", layersList);
if (this->showNotifications) {
QMessageBox::information(0, i18nc("@title:window", "Layers will be lost"), \
msg);
diff --git a/krita/plugins/formats/exr/exr_import.cc \
b/krita/plugins/formats/exr/exr_import.cc index cb57ad1..6ea9f1c 100644
--- a/krita/plugins/formats/exr/exr_import.cc
+++ b/krita/plugins/formats/exr/exr_import.cc
@@ -69,6 +69,7 @@ KisImportExportFilter::ConversionStatus exrImport::convert(const \
QByteArray&, co
switch (ib.buildImage(url)) {
case KisImageBuilder_RESULT_UNSUPPORTED:
+ case KisImageBuilder_RESULT_UNSUPPORTED_COLORSPACE:
doc->setErrorMessage(i18n("Krita does support this type of EXR file."));
return KisImportExportFilter::NotImplemented;
@@ -94,6 +95,7 @@ KisImportExportFilter::ConversionStatus exrImport::convert(const \
QByteArray&, co Q_ASSERT(ib.image());
doc -> setCurrentImage(ib.image());
return KisImportExportFilter::OK;
+
default:
break;
}
[prev in list] [next in list] [prev in thread] [next in thread]
Configure |
About |
News |
Add a list |
Sponsored by KoreLogic