Krita Source Code Documentation
Loading...
Searching...
No Matches
EXRConverter::Private Struct Reference

Public Member Functions

bool checkExtraLayersInfoConsistent (const QDomDocument &doc, std::set< std::string > exrLayerNames)
 
template<typename _T_ >
void decodeData1 (Imf::InputFile &file, ExrPaintLayerInfo &info, KisPaintLayerSP layer, int width, int xstart, int ystart, int height, Imf::PixelType ptype)
 
template<typename _T_ >
void decodeData4 (Imf::InputFile &file, ExrPaintLayerInfo &info, KisPaintLayerSP layer, int width, int xstart, int ystart, int height, Imf::PixelType ptype)
 
QString fetchExtraLayersInfo (QList< ExrPaintLayerSaveInfo > &informationObjects)
 
QDomDocument loadExtraLayersInfo (const Imf::Header &header)
 
void makeLayerNamesUnique (QList< ExrPaintLayerSaveInfo > &informationObjects)
 
 Private ()
 
void recBuildPaintLayerSaveInfo (QList< ExrPaintLayerSaveInfo > &informationObjects, const QString &name, KisGroupLayerSP parent)
 
void reportLayersNotSaved (const QSet< KisNodeSP > &layersNotSaved)
 
template<class WrapperType >
void unmultiplyAlpha (typename WrapperType::pixel_type *pixel)
 

Public Attributes

bool alphaWasModified
 
KisDocumentdoc
 
QString errorMessage
 
KisImageSP image
 
bool showNotifications
 

Detailed Description

Definition at line 126 of file exr_converter.cc.

Constructor & Destructor Documentation

◆ Private()

EXRConverter::Private::Private ( )
inline

Definition at line 127 of file exr_converter.cc.

128 : doc(0)
129 , alphaWasModified(false)
130 , showNotifications(false)
131 {}

Member Function Documentation

◆ checkExtraLayersInfoConsistent()

bool EXRConverter::Private::checkExtraLayersInfoConsistent ( const QDomDocument & doc,
std::set< std::string > exrLayerNames )

Definition at line 528 of file exr_converter.cc.

529{
530 std::set<std::string> extraInfoLayers;
531
532 QDomElement root = doc.documentElement();
533
534 KIS_ASSERT_RECOVER(!root.isNull() && root.hasChildNodes()) { return false; };
535
536 QDomElement el = root.firstChildElement();
537
538 while(!el.isNull()) {
539 KIS_ASSERT_RECOVER(el.hasAttribute(EXR_NAME)) { return false; };
540 QString layerName = el.attribute(EXR_NAME).toUtf8();
541 if (layerName != QString(HDR_LAYER)) {
542 extraInfoLayers.insert(el.attribute(EXR_NAME).toUtf8().constData());
543 }
544 el = el.nextSiblingElement();
545 }
546
547 bool result = (extraInfoLayers == exrLayerNames);
548
549 if (!result) {
550 dbgKrita << "WARNING: Krita EXR extra layers info is inconsistent!";
551 dbgKrita << ppVar(extraInfoLayers.size()) << ppVar(exrLayerNames.size());
552
553 std::set<std::string>::const_iterator it1 = extraInfoLayers.begin();
554 std::set<std::string>::const_iterator it2 = exrLayerNames.begin();
555
556 std::set<std::string>::const_iterator end1 = extraInfoLayers.end();
557
558 for (; it1 != end1; ++it1, ++it2) {
559 dbgKrita << it1->c_str() << it2->c_str();
560 }
561
562 }
563
564 return result;
565}
#define HDR_LAYER
const char EXR_NAME[]
#define KIS_ASSERT_RECOVER(cond)
Definition kis_assert.h:55
#define dbgKrita
Definition kis_debug.h:45
#define ppVar(var)
Definition kis_debug.h:155

References dbgKrita, EXR_NAME, HDR_LAYER, KIS_ASSERT_RECOVER, and ppVar.

◆ decodeData1()

template<typename _T_ >
void EXRConverter::Private::decodeData1 ( Imf::InputFile & file,
ExrPaintLayerInfo & info,
KisPaintLayerSP layer,
int width,
int xstart,
int ystart,
int height,
Imf::PixelType ptype )

Definition at line 434 of file exr_converter.cc.

435{
436 typedef typename GrayPixelWrapper<_T_>::channel_type channel_type;
437 typedef typename GrayPixelWrapper<_T_>::pixel_type pixel_type;
438
441
442 QVector<pixel_type> pixels(width * height);
443
444 Q_ASSERT(info.channelMap.contains("Y"));
445 dbgFile << "Gray -> " << info.channelMap["Y"];
446
447 bool hasAlpha = info.channelMap.contains("A");
448 dbgFile << "Has Alpha:" << hasAlpha;
449
450
451 Imf::FrameBuffer frameBuffer;
452 pixel_type* frameBufferData = (pixels.data()) - xstart - ystart * width;
453 frameBuffer.insert(
454 info.channelMap["Y"].toLatin1().constData(),
455 Imf::Slice(ptype, (char *)&frameBufferData->gray, sizeof(pixel_type) * 1, sizeof(pixel_type) * width));
456
457 if (hasAlpha) {
458 frameBuffer.insert(info.channelMap["A"].toLatin1().constData(),
459 Imf::Slice(ptype, (char *) &frameBufferData->alpha,
460 sizeof(pixel_type) * 1,
461 sizeof(pixel_type) * width));
462 }
463
464 file.setFrameBuffer(frameBuffer);
465 file.readPixels(ystart, height + ystart - 1);
466
467 pixel_type *srcPtr = pixels.data();
468
469 QRect paintRegion(xstart, ystart, width, height);
470 KisSequentialIterator it(layer->paintDevice(), paintRegion);
471 while (it.nextPixel()) {
472 if (hasAlpha) {
473 unmultiplyAlpha<GrayPixelWrapper<_T_> >(srcPtr);
474 }
475
476 pixel_type* dstPtr = reinterpret_cast<pixel_type*>(it.rawData());
477
478 dstPtr->gray = srcPtr->gray;
479 dstPtr->alpha = hasAlpha ? srcPtr->alpha : channel_type(1.0);
480
481 ++srcPtr;
482 } ;
483}
const KoID GrayAColorModelID("GRAYA", ki18n("Grayscale/Alpha"))
const KoColorSpace * colorSpace() const
virtual KoID colorModelId() const =0
#define KIS_ASSERT_RECOVER_RETURN(cond)
Definition kis_assert.h:75
#define dbgFile
Definition kis_debug.h:53
QMap< QString, QString > channelMap
first is either R, G, B or A second is the EXR channel name
KisPaintDeviceSP paintDevice

References ExrPaintLayerInfo::channelMap, KoColorSpace::colorModelId(), KisPaintDevice::colorSpace(), dbgFile, GrayAColorModelID, KIS_ASSERT_RECOVER_RETURN, KisSequentialIteratorBase< IteratorPolicy, SourcePolicy, ProgressPolicy >::nextPixel(), KisPaintLayer::paintDevice, and KisSequentialIteratorBase< IteratorPolicy, SourcePolicy, ProgressPolicy >::rawData().

◆ decodeData4()

template<typename _T_ >
void EXRConverter::Private::decodeData4 ( Imf::InputFile & file,
ExrPaintLayerInfo & info,
KisPaintLayerSP layer,
int width,
int xstart,
int ystart,
int height,
Imf::PixelType ptype )

Definition at line 378 of file exr_converter.cc.

379{
380 typedef Rgba<_T_> Rgba;
381
382 QVector<Rgba> pixels(width * height);
383
384 bool hasAlpha = info.channelMap.contains("A");
385
386 Imf::FrameBuffer frameBuffer;
387 Rgba* frameBufferData = (pixels.data()) - xstart - ystart * width;
388 frameBuffer.insert(info.channelMap["R"].toLatin1().constData(),
389 Imf::Slice(ptype, (char *) &frameBufferData->r,
390 sizeof(Rgba) * 1,
391 sizeof(Rgba) * width));
392 frameBuffer.insert(info.channelMap["G"].toLatin1().constData(),
393 Imf::Slice(ptype, (char *) &frameBufferData->g,
394 sizeof(Rgba) * 1,
395 sizeof(Rgba) * width));
396 frameBuffer.insert(info.channelMap["B"].toLatin1().constData(),
397 Imf::Slice(ptype, (char *) &frameBufferData->b,
398 sizeof(Rgba) * 1,
399 sizeof(Rgba) * width));
400 if (hasAlpha) {
401 frameBuffer.insert(info.channelMap["A"].toLatin1().constData(),
402 Imf::Slice(ptype, (char *) &frameBufferData->a,
403 sizeof(Rgba) * 1,
404 sizeof(Rgba) * width));
405 }
406
407 file.setFrameBuffer(frameBuffer);
408 file.readPixels(ystart, height + ystart - 1);
409 Rgba *rgba = pixels.data();
410
411 QRect paintRegion(xstart, ystart, width, height);
412 KisSequentialIterator it(layer->paintDevice(), paintRegion);
413 while (it.nextPixel()) {
414 if (hasAlpha) {
415 unmultiplyAlpha<RgbPixelWrapper<_T_> >(rgba);
416 }
417
418 typename KoRgbTraits<_T_>::Pixel* dst = reinterpret_cast<typename KoRgbTraits<_T_>::Pixel*>(it.rawData());
419
420 dst->red = rgba->r;
421 dst->green = rgba->g;
422 dst->blue = rgba->b;
423 if (hasAlpha) {
424 dst->alpha = rgba->a;
425 } else {
426 dst->alpha = 1.0;
427 }
428
429 ++rgba;
430 }
431}
uint32_t rgba
Definition pixels.h:44

References Rgba< _T_ >::a, KoRgbTraits< _channels_type_ >::Pixel::alpha, Rgba< _T_ >::b, KoRgbTraits< _channels_type_ >::Pixel::blue, ExrPaintLayerInfo::channelMap, Rgba< _T_ >::g, KoRgbTraits< _channels_type_ >::Pixel::green, KisSequentialIteratorBase< IteratorPolicy, SourcePolicy, ProgressPolicy >::nextPixel(), KisPaintLayer::paintDevice, Rgba< _T_ >::r, KisSequentialIteratorBase< IteratorPolicy, SourcePolicy, ProgressPolicy >::rawData(), and KoRgbTraits< _channels_type_ >::Pixel::red.

◆ fetchExtraLayersInfo()

QString EXRConverter::Private::fetchExtraLayersInfo ( QList< ExrPaintLayerSaveInfo > & informationObjects)

Definition at line 1365 of file exr_converter.cc.

1366{
1367 KIS_ASSERT_RECOVER_NOOP(!informationObjects.isEmpty());
1368
1369 if (informationObjects.size() == 1 && informationObjects[0].name == QString(HDR_LAYER) + ".") {
1370 return QString();
1371 }
1372
1373 QDomDocument doc("krita-extra-layers-info");
1374 doc.appendChild(doc.createElement("root"));
1375 QDomElement rootElement = doc.documentElement();
1376
1377 for (int i = 0; i < informationObjects.size(); i++) {
1378 ExrPaintLayerSaveInfo &info = informationObjects[i];
1379 quint32 unused;
1380 KisSaveXmlVisitor visitor(doc, rootElement, unused, QString(), false);
1381
1382 // EXR data is saved without the offset, saving code uses normal iterators
1383 // that don't know about the layer offset, hence passing `false` for saving
1384 // the offsets
1385 QDomElement el = visitor.savePaintLayerAttributes(info.layer.data(), doc, false);
1386
1387 // cut the ending '.'
1388 QString strippedName = info.name.left(info.name.size() - 1);
1389
1390 el.setAttribute(EXR_NAME, strippedName);
1391
1392 rootElement.appendChild(el);
1393 }
1394
1395 return doc.toString();
1396}
#define KIS_ASSERT_RECOVER_NOOP(cond)
Definition kis_assert.h:97
QString name
name of the layer with a "." at the end (ie "group1.group2.layer1.")
KisPaintLayerSP layer

References KisSharedPtr< T >::data(), EXR_NAME, HDR_LAYER, KIS_ASSERT_RECOVER_NOOP, ExrPaintLayerSaveInfo::layer, ExrPaintLayerSaveInfo::name, and KisSaveXmlVisitor::savePaintLayerAttributes().

◆ loadExtraLayersInfo()

QDomDocument EXRConverter::Private::loadExtraLayersInfo ( const Imf::Header & header)

Definition at line 513 of file exr_converter.cc.

514{
515 const Imf::StringAttribute *layersInfoAttribute =
516 header.findTypedAttribute<Imf::StringAttribute>(EXR_KRITA_LAYERS);
517
518 if (!layersInfoAttribute) return QDomDocument();
519
520 QString layersInfoString = QString::fromUtf8(layersInfoAttribute->value().c_str());
521
522 QDomDocument doc;
523 doc.setContent(layersInfoString);
524
525 return doc;
526}
const char EXR_KRITA_LAYERS[]

References EXR_KRITA_LAYERS.

◆ makeLayerNamesUnique()

void EXRConverter::Private::makeLayerNamesUnique ( QList< ExrPaintLayerSaveInfo > & informationObjects)

Definition at line 1212 of file exr_converter.cc.

1213{
1214 typedef QMultiMap<QString, QList<ExrPaintLayerSaveInfo>::iterator> NamesMap;
1215 NamesMap namesMap;
1216
1217 {
1218 QList<ExrPaintLayerSaveInfo>::iterator it = informationObjects.begin();
1219 QList<ExrPaintLayerSaveInfo>::iterator end = informationObjects.end();
1220
1221 for (; it != end; ++it) {
1222 namesMap.insert(it->name, it);
1223 }
1224 }
1225
1226 Q_FOREACH (const QString &key, namesMap.keys()) {
1227 if (namesMap.count(key) > 1) {
1228 KIS_ASSERT_RECOVER(key.endsWith(".")) { continue; }
1229 QString strippedName = key.left(key.size() - 1); // trim the ending dot
1230 int nameCounter = 0;
1231
1232 NamesMap::iterator it = namesMap.find(key);
1233 NamesMap::iterator end = namesMap.end();
1234
1235 for (; it != end; ++it) {
1236 QString newName =
1237 QString("%1_%2.")
1238 .arg(strippedName)
1239 .arg(nameCounter++);
1240
1241 it.value()->name = newName;
1242
1243 QList<QString>::iterator channelsIt = it.value()->channels.begin();
1244 QList<QString>::iterator channelsEnd = it.value()->channels.end();
1245
1246 for (; channelsIt != channelsEnd; ++channelsIt) {
1247 channelsIt->replace(key, newName);
1248 }
1249 }
1250 }
1251 }
1252
1253}

References KIS_ASSERT_RECOVER.

◆ recBuildPaintLayerSaveInfo()

void EXRConverter::Private::recBuildPaintLayerSaveInfo ( QList< ExrPaintLayerSaveInfo > & informationObjects,
const QString & name,
KisGroupLayerSP parent )

The EXR can store paint and group layers only. The rest will go to /dev/null :(

Definition at line 1255 of file exr_converter.cc.

1256{
1257 QSet<KisNodeSP> layersNotSaved;
1258
1259 for (uint i = 0; i < parent->childCount(); ++i) {
1260 KisNodeSP node = parent->at(i);
1261
1262 if (KisPaintLayerSP paintLayer = dynamic_cast<KisPaintLayer*>(node.data())) {
1263 QMap<QString, QString> current2original;
1264
1265 if (paintLayer->metaData()->containsEntry(KisMetaData::SchemaRegistry::instance()->create("http://krita.org/exrchannels/1.0/" , "exrchannels"), "channelsmap")) {
1266
1267 const KisMetaData::Entry& entry = paintLayer->metaData()->getEntry(KisMetaData::SchemaRegistry::instance()->create("http://krita.org/exrchannels/1.0/" , "exrchannels"), "channelsmap");
1268 QList< KisMetaData::Value> values = entry.value().asArray();
1269
1270 Q_FOREACH (const KisMetaData::Value& value, values) {
1271 QMap<QString, KisMetaData::Value> map = value.asStructure();
1272 if (map.contains("original") && map.contains("current")) {
1273 current2original[map["current"].toString()] = map["original"].toString();
1274 }
1275 }
1276
1277 }
1278
1280 info.name = name + paintLayer->name() + '.';
1281 info.layer = paintLayer;
1282 info.layerDevice = wrapLayerDevice(paintLayer->paintDevice());
1283
1284 if (info.name == QString(HDR_LAYER) + ".") {
1285 info.channels.push_back("R");
1286 info.channels.push_back("G");
1287 info.channels.push_back("B");
1288 info.channels.push_back("A");
1289 }
1290 else {
1291
1293 info.channels.push_back(info.name + remap(current2original, "R"));
1294 info.channels.push_back(info.name + remap(current2original, "G"));
1295 info.channels.push_back(info.name + remap(current2original, "B"));
1296 info.channels.push_back(info.name + remap(current2original, "A"));
1297 }
1298 else if (info.layerDevice->colorSpace()->colorModelId() == GrayAColorModelID) {
1299 info.channels.push_back(info.name + remap(current2original, "Y"));
1300 info.channels.push_back(info.name + remap(current2original, "A"));
1301 } else if (info.layerDevice->colorSpace()->colorModelId() == XYZAColorModelID) {
1302 info.channels.push_back(info.name + remap(current2original, "X"));
1303 info.channels.push_back(info.name + remap(current2original, "Y"));
1304 info.channels.push_back(info.name + remap(current2original, "Z"));
1305 info.channels.push_back(info.name + remap(current2original, "A"));
1306 }
1307 }
1308
1310 info.pixelType = Imf::HALF;
1311 }
1313 info.pixelType = Imf::FLOAT;
1314 }
1315 else {
1316 info.pixelType = Imf::NUM_PIXELTYPES;
1317 }
1318
1319 if (info.pixelType < Imf::NUM_PIXELTYPES) {
1320 dbgFile << "Going to save layer" << info.name;
1321 informationObjects.push_back(info);
1322 }
1323 else {
1324 warnFile << "Will not save layer" << info.name;
1325 layersNotSaved << node;
1326 }
1327
1328 }
1329 else if (KisGroupLayerSP groupLayer = dynamic_cast<KisGroupLayer*>(node.data())) {
1330 recBuildPaintLayerSaveInfo(informationObjects, name + groupLayer->name() + '.', groupLayer);
1331 }
1332 else {
1337 layersNotSaved.insert(node);
1338 }
1339 }
1340
1341 if (!layersNotSaved.isEmpty()) {
1342 reportLayersNotSaved(layersNotSaved);
1343 }
1344}
float value(const T *src, size_t ch)
const KoID Float32BitsColorDepthID("F32", ki18n("32-bit float/channel"))
const KoID XYZAColorModelID("XYZA", ki18n("XYZ/Alpha"))
const KoID Float16BitsColorDepthID("F16", ki18n("16-bit float/channel"))
const KoID RGBAColorModelID("RGBA", ki18n("RGB/Alpha"))
unsigned int uint
const KisMetaData::Value & value() const
static KisMetaData::SchemaRegistry * instance()
const KisMetaData::Schema * create(const QString &uri, const QString &prefix)
QList< KisMetaData::Value > asArray() const
virtual KoID colorDepthId() const =0
QString remap(const QMap< QString, QString > &current2original, const QString &current)
KisPaintDeviceSP wrapLayerDevice(KisPaintDeviceSP device)
#define warnFile
Definition kis_debug.h:95
const char * name(StandardAction id)
ChildIterator< value_type, is_const > parent(const ChildIterator< value_type, is_const > &it)
Definition KisForest.h:327
QPainterPath create(const char32_t codepoint, double height)
Creates a tofu missing glyph indicator representing the provided Unicode codepoint.
void recBuildPaintLayerSaveInfo(QList< ExrPaintLayerSaveInfo > &informationObjects, const QString &name, KisGroupLayerSP parent)
void reportLayersNotSaved(const QSet< KisNodeSP > &layersNotSaved)
QList< QString > channels
KisPaintDeviceSP layerDevice
Imf::PixelType pixelType

References KisMetaData::Value::asArray(), ExrPaintLayerSaveInfo::channels, KoColorSpace::colorDepthId(), KoColorSpace::colorModelId(), KisPaintDevice::colorSpace(), KisMetaData::SchemaRegistry::create(), KisSharedPtr< T >::data(), dbgFile, Float16BitsColorDepthID, Float32BitsColorDepthID, GrayAColorModelID, HDR_LAYER, KisMetaData::SchemaRegistry::instance(), ExrPaintLayerSaveInfo::layer, ExrPaintLayerSaveInfo::layerDevice, ExrPaintLayerSaveInfo::name, ExrPaintLayerSaveInfo::pixelType, remap(), RGBAColorModelID, KisMetaData::Entry::value(), value(), warnFile, wrapLayerDevice(), and XYZAColorModelID.

◆ reportLayersNotSaved()

void EXRConverter::Private::reportLayersNotSaved ( const QSet< KisNodeSP > & layersNotSaved)

Definition at line 1346 of file exr_converter.cc.

1347{
1348 QString layersList;
1349 QTextStream textStream(&layersList);
1351
1352 Q_FOREACH (KisNodeSP node, layersNotSaved) {
1353 textStream << "<li>" << i18nc("@item:unsupported-node-message", "%1 (type: \"%2\")", node->name(), node->metaObject()->className()) << "</li>";
1354 }
1355
1356 QString msg =
1357 i18nc("@info",
1358 "<p>The following layers have a type that is not supported by EXR format:</p>"
1359 "<r><ul>%1</ul></p>"
1360 "<p><warning>these layers have <b>not</b> been saved to the final EXR file</warning></p>", layersList);
1361
1362 errorMessage = msg;
1363}
void setUtf8OnStream(QTextStream &stream)
QString name() const

References EXRConverter::errorMessage(), KisBaseNode::name(), and KisPortingUtils::setUtf8OnStream().

◆ unmultiplyAlpha()

template<class WrapperType >
void EXRConverter::Private::unmultiplyAlpha ( typename WrapperType::pixel_type * pixel)

Division by a tiny alpha may result in an overflow of half value. That is why we use safe iterative approach.

Definition at line 318 of file exr_converter.cc.

319{
320 typedef typename WrapperType::pixel_type pixel_type;
321 typedef typename WrapperType::channel_type channel_type;
322
323 WrapperType srcPixel(*pixel);
324
325 if (!srcPixel.checkMultipliedColorsConsistent()) {
326
327 channel_type newAlpha = srcPixel.alpha();
328
329 pixel_type __dstPixelData;
330 WrapperType dstPixel(__dstPixelData);
331
336 while (1) {
337 dstPixel.setUnmultiplied(srcPixel.pixel, newAlpha);
338
339 if (dstPixel.checkUnmultipliedColorsConsistent(srcPixel.pixel)) {
340 break;
341 }
342
343 newAlpha += alphaEpsilon<channel_type>();
344 alphaWasModified = true;
345 }
346
347 *pixel = dstPixel.pixel;
348
349
350 } else if (srcPixel.alpha() > 0.0) {
351 srcPixel.setUnmultiplied(srcPixel.pixel, srcPixel.alpha());
352 }
353}

References alphaWasModified.

Member Data Documentation

◆ alphaWasModified

bool EXRConverter::Private::alphaWasModified

Definition at line 136 of file exr_converter.cc.

◆ doc

KisDocument* EXRConverter::Private::doc

Definition at line 134 of file exr_converter.cc.

◆ errorMessage

QString EXRConverter::Private::errorMessage

Definition at line 139 of file exr_converter.cc.

◆ image

KisImageSP EXRConverter::Private::image

Definition at line 133 of file exr_converter.cc.

◆ showNotifications

bool EXRConverter::Private::showNotifications

Definition at line 137 of file exr_converter.cc.


The documentation for this struct was generated from the following file: