1 /*
2  *  Copyright (c) 2005 Adrian Page <adrian@pagenet.plus.com>
3  *  Copyright (c) 2010 Cyrille Berger <cberger@cberger.net>
4  *
5  *  This program is free software; you can redistribute it and/or modify
6  *  it under the terms of the GNU General Public License as published by
7  *  the Free Software Foundation; either version 2 of the License, or
8  *  (at your option) any later version.
9  *
10  *  This program is distributed in the hope that it will be useful,
11  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  *  GNU General Public License for more details.
14  *
15  *  You should have received a copy of the GNU General Public License
16  *  along with this program; if not, write to the Free Software
17  *  Foundation, Inc., 51 Franklin Street, Fifth Floor,
18  * Boston, MA 02110-1301, USA.
19  */
20 
21 #include "exr_converter.h"
22 
23 #include <half.h>
24 
25 #include <ImfAttribute.h>
26 #include <ImfChannelList.h>
27 #include <ImfFrameBuffer.h>
28 #include <ImfHeader.h>
29 #include <ImfInputFile.h>
30 #include <ImfOutputFile.h>
31 
32 #include <ImfStringAttribute.h>
33 #include "exr_extra_tags.h"
34 
35 #include <QApplication>
36 #include <QMessageBox>
37 #include <QDomDocument>
38 #include <QThread>
39 
40 #include <QFileInfo>
41 
42 #include <KoColorSpaceRegistry.h>
43 #include <KoCompositeOpRegistry.h>
44 #include <KoColorSpaceTraits.h>
45 #include <KoColorModelStandardIds.h>
46 #include <KoColor.h>
47 #include <KoColorProfile.h>
48 
49 #include <KisDocument.h>
50 #include <kis_group_layer.h>
51 #include <kis_image.h>
52 #include <kis_paint_device.h>
53 #include <kis_paint_layer.h>
54 #include <kis_transaction.h>
55 #include "kis_iterator_ng.h"
56 #include <kis_exr_layers_sorter.h>
57 
58 #include <kis_meta_data_entry.h>
59 #include <kis_meta_data_schema.h>
60 #include <kis_meta_data_schema_registry.h>
61 #include <kis_meta_data_store.h>
62 #include <kis_meta_data_value.h>
63 
64 #include "kis_kra_savexml_visitor.h"
65 
66 #include <KisImportExportAdditionalChecks.h>
67 
68 // Do not translate!
69 #define HDR_LAYER "HDR Layer"
70 
71 template<typename _T_>
72 struct Rgba {
73     _T_ r;
74     _T_ g;
75     _T_ b;
76     _T_ a;
77 };
78 
79 struct ExrGroupLayerInfo;
80 
81 struct ExrLayerInfoBase {
ExrLayerInfoBaseExrLayerInfoBase82     ExrLayerInfoBase() : colorSpace(0), parent(0) {
83     }
84     const KoColorSpace* colorSpace;
85     QString name;
86     const ExrGroupLayerInfo* parent;
87 };
88 
89 struct ExrGroupLayerInfo : public ExrLayerInfoBase {
ExrGroupLayerInfoExrGroupLayerInfo90     ExrGroupLayerInfo() : groupLayer(0) {}
91     KisGroupLayerSP groupLayer;
92 };
93 
94 enum ImageType {
95     IT_UNKNOWN,
96     IT_FLOAT16,
97     IT_FLOAT32,
98     IT_UNSUPPORTED
99 };
100 
101 struct ExrPaintLayerInfo : public ExrLayerInfoBase {
ExrPaintLayerInfoExrPaintLayerInfo102     ExrPaintLayerInfo()
103         : imageType(IT_UNKNOWN)
104     {
105     }
106 
107     ImageType imageType;
108     QMap< QString, QString> channelMap; ///< first is either R, G, B or A second is the EXR channel name
109 
110     struct Remap {
RemapExrPaintLayerInfo::Remap111         Remap(const QString& _original, const QString& _current) : original(_original), current(_current) {
112         }
113         QString original;
114         QString current;
115     };
116 
117     QList< Remap > remappedChannels; ///< this is used to store in the metadata the mapping between exr channel name, and channels used in Krita
118     void updateImageType(ImageType channelType);
119 };
120 
updateImageType(ImageType channelType)121 void ExrPaintLayerInfo::updateImageType(ImageType channelType)
122 {
123     if (imageType == IT_UNKNOWN) {
124         imageType = channelType;
125     }
126     else if (imageType != channelType) {
127         imageType = IT_UNSUPPORTED;
128     }
129 }
130 
131 struct ExrPaintLayerSaveInfo {
132     QString name; ///< name of the layer with a "." at the end (ie "group1.group2.layer1.")
133     KisPaintDeviceSP layerDevice;
134     KisPaintLayerSP layer;
135     QList<QString> channels;
136     Imf::PixelType pixelType;
137 };
138 
139 struct EXRConverter::Private {
PrivateEXRConverter::Private140     Private()
141         : doc(0)
142         , alphaWasModified(false)
143         , showNotifications(false)
144     {}
145 
146     KisImageSP image;
147     KisDocument *doc;
148 
149     bool alphaWasModified;
150     bool showNotifications;
151 
152     QString errorMessage;
153 
154     template <class WrapperType>
155     void unmultiplyAlpha(typename WrapperType::pixel_type *pixel);
156 
157     template<typename _T_>
158     void decodeData4(Imf::InputFile& file, ExrPaintLayerInfo& info, KisPaintLayerSP layer, int width, int xstart, int ystart, int height, Imf::PixelType ptype);
159 
160     template<typename _T_>
161     void decodeData1(Imf::InputFile& file, ExrPaintLayerInfo& info, KisPaintLayerSP layer, int width, int xstart, int ystart, int height, Imf::PixelType ptype);
162 
163 
164     QDomDocument loadExtraLayersInfo(const Imf::Header &header);
165     bool checkExtraLayersInfoConsistent(const QDomDocument &doc, std::set<std::string> exrLayerNames);
166     void makeLayerNamesUnique(QList<ExrPaintLayerSaveInfo>& informationObjects);
167     void recBuildPaintLayerSaveInfo(QList<ExrPaintLayerSaveInfo>& informationObjects, const QString& name, KisGroupLayerSP parent);
168     void reportLayersNotSaved(const QSet<KisNodeSP> &layersNotSaved);
169     QString fetchExtraLayersInfo(QList<ExrPaintLayerSaveInfo>& informationObjects);
170 };
171 
EXRConverter(KisDocument * doc,bool showNotifications)172 EXRConverter::EXRConverter(KisDocument *doc, bool showNotifications)
173     : d(new Private)
174 {
175     d->doc = doc;
176     d->showNotifications = showNotifications;
177 
178     // Set thread count for IlmImf library
179     Imf::setGlobalThreadCount(QThread::idealThreadCount());
180     dbgFile << "EXR Threadcount was set to: " << QThread::idealThreadCount();
181 }
182 
~EXRConverter()183 EXRConverter::~EXRConverter()
184 {
185 }
186 
imfTypeToKisType(Imf::PixelType type)187 ImageType imfTypeToKisType(Imf::PixelType type)
188 {
189     switch (type) {
190     case Imf::UINT:
191     case Imf::NUM_PIXELTYPES:
192         return IT_UNSUPPORTED;
193     case Imf::HALF:
194         return IT_FLOAT16;
195     case Imf::FLOAT:
196         return IT_FLOAT32;
197     default:
198         qFatal("Out of bound enum");
199         return IT_UNKNOWN;
200     }
201 }
202 
kisTypeToColorSpace(QString colorModelID,ImageType imageType)203 const KoColorSpace *kisTypeToColorSpace(QString colorModelID, ImageType imageType)
204 {
205 
206     QString colorDepthID = "UNKNOWN";
207     switch(imageType) {
208     case IT_FLOAT16:
209         colorDepthID = Float16BitsColorDepthID.id();
210         break;
211     case IT_FLOAT32:
212         colorDepthID = Float32BitsColorDepthID.id();
213         break;
214     default:
215         return 0;
216     };
217 
218     const QString colorSpaceId = KoColorSpaceRegistry::instance()->colorSpaceId(colorModelID, colorDepthID);
219     const QString profileName = KisConfig(false).readEntry("ExrDefaultColorProfile", KoColorSpaceRegistry::instance()->defaultProfileForColorSpace(colorSpaceId));
220 
221     return KoColorSpaceRegistry::instance()->colorSpace(colorModelID, colorDepthID, profileName);
222 
223 }
224 
225 template <typename T>
alphaEpsilon()226 static inline T alphaEpsilon()
227 {
228     return static_cast<T>(HALF_EPSILON);
229 }
230 
231 template <typename T>
alphaNoiseThreshold()232 static inline T alphaNoiseThreshold()
233 {
234     return static_cast<T>(0.01); // 1%
235 }
236 
qFuzzyCompare(half p1,half p2)237 static inline bool qFuzzyCompare(half p1, half p2)
238 {
239     return std::abs(p1 - p2) < float(HALF_EPSILON);
240 }
241 
qFuzzyIsNull(half h)242 static inline bool qFuzzyIsNull(half h)
243 {
244     return std::abs(h) < float(HALF_EPSILON);
245 }
246 
247 template <typename T>
248 struct RgbPixelWrapper
249 {
250     typedef T channel_type;
251     typedef Rgba<T> pixel_type;
252 
RgbPixelWrapperRgbPixelWrapper253     RgbPixelWrapper(Rgba<T> &_pixel) : pixel(_pixel) {}
254 
alphaRgbPixelWrapper255     inline T alpha() const {
256         return pixel.a;
257     }
258 
checkMultipliedColorsConsistentRgbPixelWrapper259     inline bool checkMultipliedColorsConsistent() const {
260         return !(std::abs(pixel.a) < alphaEpsilon<T>() &&
261                  (!qFuzzyIsNull(pixel.r) ||
262                   !qFuzzyIsNull(pixel.g) ||
263                   !qFuzzyIsNull(pixel.b)));
264     }
265 
checkUnmultipliedColorsConsistentRgbPixelWrapper266     inline bool checkUnmultipliedColorsConsistent(const Rgba<T> &mult) const {
267         const T alpha = std::abs(pixel.a);
268 
269         return alpha >= alphaNoiseThreshold<T>() ||
270                 (qFuzzyCompare(T(pixel.r * alpha), mult.r) &&
271                  qFuzzyCompare(T(pixel.g * alpha), mult.g) &&
272                  qFuzzyCompare(T(pixel.b * alpha), mult.b));
273     }
274 
setUnmultipliedRgbPixelWrapper275     inline void setUnmultiplied(const Rgba<T> &mult, T newAlpha) {
276         const T absoluteAlpha = std::abs(newAlpha);
277 
278         pixel.r = mult.r / absoluteAlpha;
279         pixel.g = mult.g / absoluteAlpha;
280         pixel.b = mult.b / absoluteAlpha;
281         pixel.a = newAlpha;
282     }
283 
284     Rgba<T> &pixel;
285 };
286 
287 template <typename T>
288 struct GrayPixelWrapper
289 {
290     typedef T channel_type;
291     typedef typename KoGrayTraits<T>::Pixel pixel_type;
292 
GrayPixelWrapperGrayPixelWrapper293     GrayPixelWrapper(pixel_type &_pixel) : pixel(_pixel) {}
294 
alphaGrayPixelWrapper295     inline T alpha() const {
296         return pixel.alpha;
297     }
298 
checkMultipliedColorsConsistentGrayPixelWrapper299     inline bool checkMultipliedColorsConsistent() const {
300         return !(std::abs(pixel.alpha) < alphaEpsilon<T>() &&
301                  !qFuzzyIsNull(pixel.gray));
302     }
303 
checkUnmultipliedColorsConsistentGrayPixelWrapper304     inline bool checkUnmultipliedColorsConsistent(const pixel_type &mult) const {
305         const T alpha = std::abs(pixel.alpha);
306 
307         return alpha >= alphaNoiseThreshold<T>() ||
308                 qFuzzyCompare(T(pixel.gray * alpha), mult.gray);
309     }
310 
setUnmultipliedGrayPixelWrapper311     inline void setUnmultiplied(const pixel_type &mult, T newAlpha) {
312         const T absoluteAlpha = std::abs(newAlpha);
313 
314         pixel.gray = mult.gray / absoluteAlpha;
315         pixel.alpha = newAlpha;
316     }
317 
318     pixel_type &pixel;
319 };
320 
321 template <class WrapperType>
unmultiplyAlpha(typename WrapperType::pixel_type * pixel)322 void EXRConverter::Private::unmultiplyAlpha(typename WrapperType::pixel_type *pixel)
323 {
324     typedef typename WrapperType::pixel_type pixel_type;
325     typedef typename WrapperType::channel_type channel_type;
326 
327     WrapperType srcPixel(*pixel);
328 
329     if (!srcPixel.checkMultipliedColorsConsistent()) {
330 
331         channel_type newAlpha = srcPixel.alpha();
332 
333         pixel_type __dstPixelData;
334         WrapperType dstPixel(__dstPixelData);
335 
336         /**
337          * Division by a tiny alpha may result in an overflow of half
338          * value. That is why we use safe iterational approach.
339          */
340         while (1) {
341             dstPixel.setUnmultiplied(srcPixel.pixel, newAlpha);
342 
343             if (dstPixel.checkUnmultipliedColorsConsistent(srcPixel.pixel)) {
344                 break;
345             }
346 
347             newAlpha += alphaEpsilon<channel_type>();
348             alphaWasModified = true;
349         }
350 
351         *pixel = dstPixel.pixel;
352 
353 
354     } else if (srcPixel.alpha() > 0.0) {
355         srcPixel.setUnmultiplied(srcPixel.pixel, srcPixel.alpha());
356     }
357 }
358 
359 template <typename T, typename Pixel, int size, int alphaPos>
multiplyAlpha(Pixel * pixel)360 void multiplyAlpha(Pixel *pixel)
361 {
362     if (alphaPos >= 0) {
363         T alpha = pixel->data[alphaPos];
364 
365         if (alpha > 0.0) {
366             for (int i = 0; i < size; ++i) {
367                 if (i != alphaPos) {
368                     pixel->data[i] *= alpha;
369                 }
370             }
371 
372             pixel->data[alphaPos] = alpha;
373         }
374     }
375 }
376 
377 template<typename _T_>
decodeData4(Imf::InputFile & file,ExrPaintLayerInfo & info,KisPaintLayerSP layer,int width,int xstart,int ystart,int height,Imf::PixelType ptype)378 void EXRConverter::Private::decodeData4(Imf::InputFile& file, ExrPaintLayerInfo& info, KisPaintLayerSP layer, int width, int xstart, int ystart, int height, Imf::PixelType ptype)
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 }
432 
433 template<typename _T_>
decodeData1(Imf::InputFile & file,ExrPaintLayerInfo & info,KisPaintLayerSP layer,int width,int xstart,int ystart,int height,Imf::PixelType ptype)434 void EXRConverter::Private::decodeData1(Imf::InputFile& file, ExrPaintLayerInfo& info, KisPaintLayerSP layer, int width, int xstart, int ystart, int height, Imf::PixelType ptype)
435 {
436     typedef typename GrayPixelWrapper<_T_>::channel_type channel_type;
437     typedef typename GrayPixelWrapper<_T_>::pixel_type pixel_type;
438 
439     KIS_ASSERT_RECOVER_RETURN(
440                 layer->paintDevice()->colorSpace()->colorModelId() == GrayAColorModelID);
441 
442     QVector<pixel_type> pixels(width * height);
443 
444     Q_ASSERT(info.channelMap.contains("G"));
445     dbgFile << "G -> " << info.channelMap["G"];
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(info.channelMap["G"].toLatin1().constData(),
454             Imf::Slice(ptype, (char *) &frameBufferData->gray,
455                        sizeof(pixel_type) * 1,
456                        sizeof(pixel_type) * width));
457 
458     if (hasAlpha) {
459         frameBuffer.insert(info.channelMap["A"].toLatin1().constData(),
460                 Imf::Slice(ptype, (char *) &frameBufferData->alpha,
461                            sizeof(pixel_type) * 1,
462                            sizeof(pixel_type) * width));
463     }
464 
465     file.setFrameBuffer(frameBuffer);
466     file.readPixels(ystart, height + ystart - 1);
467 
468     pixel_type *srcPtr = pixels.data();
469 
470     QRect paintRegion(xstart, ystart, width, height);
471     KisSequentialIterator it(layer->paintDevice(), paintRegion);
472     while (it.nextPixel()) {
473         if (hasAlpha) {
474             unmultiplyAlpha<GrayPixelWrapper<_T_> >(srcPtr);
475         }
476 
477         pixel_type* dstPtr = reinterpret_cast<pixel_type*>(it.rawData());
478 
479         dstPtr->gray = srcPtr->gray;
480         dstPtr->alpha = hasAlpha ? srcPtr->alpha : channel_type(1.0);
481 
482         ++srcPtr;
483     } ;
484 }
485 
recCheckGroup(const ExrGroupLayerInfo & group,QStringList list,int idx1,int idx2)486 bool recCheckGroup(const ExrGroupLayerInfo& group, QStringList list, int idx1, int idx2)
487 {
488     if (idx1 > idx2) return true;
489     if (group.name == list[idx2]) {
490         return recCheckGroup(*group.parent, list, idx1, idx2 - 1);
491     }
492     return false;
493 }
494 
searchGroup(QList<ExrGroupLayerInfo> * groups,QStringList list,int idx1,int idx2)495 ExrGroupLayerInfo* searchGroup(QList<ExrGroupLayerInfo>* groups, QStringList list, int idx1, int idx2)
496 {
497     if (idx1 > idx2) {
498         return 0;
499     }
500     // Look for the group
501     for (int i = 0; i < groups->size(); ++i) {
502         if (recCheckGroup(groups->at(i), list, idx1, idx2)) {
503             return &(*groups)[i];
504         }
505     }
506     // Create the group
507     ExrGroupLayerInfo info;
508     info.name = list.at(idx2);
509     info.parent = searchGroup(groups, list, idx1, idx2 - 1);
510     groups->append(info);
511     return &groups->last();
512 }
513 
loadExtraLayersInfo(const Imf::Header & header)514 QDomDocument EXRConverter::Private::loadExtraLayersInfo(const Imf::Header &header)
515 {
516     const Imf::StringAttribute *layersInfoAttribute =
517             header.findTypedAttribute<Imf::StringAttribute>(EXR_KRITA_LAYERS);
518 
519     if (!layersInfoAttribute) return QDomDocument();
520 
521     QString layersInfoString = QString::fromUtf8(layersInfoAttribute->value().c_str());
522 
523     QDomDocument doc;
524     doc.setContent(layersInfoString);
525 
526     return doc;
527 }
528 
checkExtraLayersInfoConsistent(const QDomDocument & doc,std::set<std::string> exrLayerNames)529 bool EXRConverter::Private::checkExtraLayersInfoConsistent(const QDomDocument &doc, std::set<std::string> exrLayerNames)
530 {
531     std::set<std::string> extraInfoLayers;
532 
533     QDomElement root = doc.documentElement();
534 
535     KIS_ASSERT_RECOVER(!root.isNull() && root.hasChildNodes()) { return false; };
536 
537     QDomElement el = root.firstChildElement();
538 
539     while(!el.isNull()) {
540         KIS_ASSERT_RECOVER(el.hasAttribute(EXR_NAME)) { return false; };
541         QString layerName = el.attribute(EXR_NAME).toUtf8();
542         if (layerName != QString(HDR_LAYER)) {
543             extraInfoLayers.insert(el.attribute(EXR_NAME).toUtf8().constData());
544         }
545         el = el.nextSiblingElement();
546     }
547 
548     bool result = (extraInfoLayers == exrLayerNames);
549 
550     if (!result) {
551         dbgKrita << "WARINING: Krita EXR extra layers info is inconsistent!";
552         dbgKrita << ppVar(extraInfoLayers.size()) << ppVar(exrLayerNames.size());
553 
554         std::set<std::string>::const_iterator it1 = extraInfoLayers.begin();
555         std::set<std::string>::const_iterator it2 = exrLayerNames.begin();
556 
557         std::set<std::string>::const_iterator end1 = extraInfoLayers.end();
558 
559         for (; it1 != end1; ++it1, ++it2) {
560             dbgKrita << it1->c_str() << it2->c_str();
561         }
562 
563     }
564 
565     return result;
566 }
567 
decode(const QString & filename)568 KisImportExportErrorCode EXRConverter::decode(const QString &filename)
569 {
570     try {
571         Imf::InputFile file(filename.toUtf8());
572 
573         Imath::Box2i dw = file.header().dataWindow();
574         Imath::Box2i displayWindow = file.header().displayWindow();
575 
576         int width = dw.max.x - dw.min.x + 1;
577         int height = dw.max.y - dw.min.y + 1;
578         int dx = dw.min.x;
579         int dy = dw.min.y;
580 
581         // Display the attributes of a file
582         for (Imf::Header::ConstIterator it = file.header().begin();
583              it != file.header().end(); ++it) {
584             dbgFile << "Attribute: " << it.name() << " type: " << it.attribute().typeName();
585         }
586 
587         // fetch Krita's extra layer info, which might have been stored previously
588         QDomDocument extraLayersInfo = d->loadExtraLayersInfo(file.header());
589 
590         // Construct the list of LayerInfo
591 
592         QList<ExrPaintLayerInfo> informationObjects;
593         QList<ExrGroupLayerInfo> groups;
594 
595         ImageType imageType = IT_UNKNOWN;
596 
597         const Imf::ChannelList &channels = file.header().channels();
598         std::set<std::string> layerNames;
599         channels.layers(layerNames);
600 
601         if (!extraLayersInfo.isNull() &&
602                 !d->checkExtraLayersInfoConsistent(extraLayersInfo, layerNames)) {
603 
604             // it is inconsistent anyway
605             extraLayersInfo = QDomDocument();
606         }
607 
608         // Check if there are A, R, G, B channels
609 
610         dbgFile << "Checking for ARGB channels, they can occur in single-layer _or_ multi-layer images:";
611         ExrPaintLayerInfo info;
612         bool topLevelRGBFound = false;
613         info.name = HDR_LAYER;
614 
615         QStringList topLevelChannelNames = QStringList() << "A" << "R" << "G" << "B"
616                                                          << ".A" << ".R" << ".G" << ".B"
617                                                          << "A." << "R." << "G." << "B."
618                                                          << "A." << "R." << "G." << "B."
619                                                          << ".alpha" << ".red" << ".green" << ".blue";
620 
621         for (Imf::ChannelList::ConstIterator i = channels.begin(); i != channels.end(); ++i) {
622             const Imf::Channel &channel = i.channel();
623             dbgFile << "Channel name = " << i.name() << " type = " << channel.type;
624 
625             QString qname = i.name();
626             if (topLevelChannelNames.contains(qname)) {
627                 topLevelRGBFound = true;
628                 dbgFile << "Found top-level channel" << qname;
629                 info.channelMap[qname] = qname;
630                 info.updateImageType(imfTypeToKisType(channel.type));
631             }
632             // Channel names that don't contain a "." or that contain a
633             // "." only at the beginning or at the end are not considered
634             // to be part of any layer.
635             else if (!qname.contains('.')
636                      || !qname.mid(1).contains('.')
637                      || !qname.left(qname.size() - 1).contains('.')) {
638                 warnFile << "Found a top-level channel that is not part of the rendered image" << qname << ". Krita will not load this channel.";
639             }
640         }
641         if (topLevelRGBFound) {
642             dbgFile << "Toplevel layer" << info.name << ":Image type:" << imageType << "Layer type" << info.imageType;
643             informationObjects.push_back(info);
644             imageType = info.imageType;
645         }
646 
647         dbgFile << "Extra layers:" << layerNames.size();
648 
649         for (std::set<std::string>::const_iterator i = layerNames.begin();i != layerNames.end(); ++i) {
650 
651             info = ExrPaintLayerInfo();
652 
653             dbgFile << "layer name = " << i->c_str();
654             info.name = i->c_str();
655             Imf::ChannelList::ConstIterator layerBegin, layerEnd;
656             channels.channelsInLayer(*i, layerBegin, layerEnd);
657             for (Imf::ChannelList::ConstIterator j = layerBegin;
658                  j != layerEnd; ++j) {
659                 const Imf::Channel &channel = j.channel();
660 
661                 info.updateImageType(imfTypeToKisType(channel.type));
662 
663                 QString qname = j.name();
664                 QStringList list = qname.split('.');
665                 QString layersuffix = list.last();
666 
667                 dbgFile << "\tchannel " << j.name() << "suffix" << layersuffix << " type = " << channel.type;
668 
669                 // Nuke writes the channels for sublayers as .red instead of .R, so convert those.
670                 // See https://bugs.kde.org/show_bug.cgi?id=393771
671                 if (topLevelChannelNames.contains("." + layersuffix)) {
672                     layersuffix = layersuffix.at(0).toUpper();
673                 }
674                 dbgFile << "\t\tsuffix" << layersuffix;
675 
676 
677                 if (list.size() > 1) {
678                     info.name = list[list.size()-2];
679                     info.parent = searchGroup(&groups, list, 0, list.size() - 3);
680                 }
681 
682                 info.channelMap[layersuffix] = qname;
683             }
684 
685             if (info.imageType != IT_UNKNOWN && info.imageType != IT_UNSUPPORTED) {
686                 informationObjects.push_back(info);
687                 if (imageType < info.imageType) {
688                     imageType = info.imageType;
689                 }
690             }
691         }
692 
693         dbgFile << "File has" << informationObjects.size() << "layer(s)";
694 
695         // Set the colorspaces
696         for (int i = 0; i < informationObjects.size(); ++i) {
697             ExrPaintLayerInfo& info = informationObjects[i];
698             QString modelId;
699 
700             if (info.channelMap.size() == 1) {
701                 modelId = GrayAColorModelID.id();
702                 QString key = info.channelMap.begin().key();
703                 if (key != "G") {
704                     info.remappedChannels.push_back(ExrPaintLayerInfo::Remap(key, "G"));
705                     QString channel =  info.channelMap.begin().value();
706                     info.channelMap.clear();
707                     info.channelMap["G"] = channel;
708                 }
709             }
710             else if (info.channelMap.size() == 2) {
711                 modelId = GrayAColorModelID.id();
712 
713                 QMap<QString,QString>::const_iterator it = info.channelMap.constBegin();
714                 QMap<QString,QString>::const_iterator end = info.channelMap.constEnd();
715 
716                 QString failingChannelKey;
717 
718                 for (; it != end; ++it) {
719                     if (it.key() != "G" && it.key() != "A") {
720                         failingChannelKey = it.key();
721                         break;
722                     }
723                 }
724 
725                 info.remappedChannels.push_back(
726                             ExrPaintLayerInfo::Remap(failingChannelKey, "G"));
727 
728                 QString failingChannelValue = info.channelMap[failingChannelKey];
729                 info.channelMap.remove(failingChannelKey);
730                 info.channelMap["G"] = failingChannelValue;
731 
732             }
733             else if (info.channelMap.size() == 3 || info.channelMap.size() == 4) {
734 
735                 if (info.channelMap.contains("R") && info.channelMap.contains("G") && info.channelMap.contains("B")) {
736                     modelId = RGBAColorModelID.id();
737                 }
738                 else if (info.channelMap.contains("X") && info.channelMap.contains("Y") && info.channelMap.contains("Z")) {
739                     modelId = XYZAColorModelID.id();
740                     QMap<QString, QString> newChannelMap;
741                     if (info.channelMap.contains("W")) {
742                         newChannelMap["A"] = info.channelMap["W"];
743                         info.remappedChannels.push_back(ExrPaintLayerInfo::Remap("W", "A"));
744                         info.remappedChannels.push_back(ExrPaintLayerInfo::Remap("X", "X"));
745                         info.remappedChannels.push_back(ExrPaintLayerInfo::Remap("Y", "Y"));
746                         info.remappedChannels.push_back(ExrPaintLayerInfo::Remap("Z", "Z"));
747                     } else if (info.channelMap.contains("A")) {
748                         newChannelMap["A"] = info.channelMap["A"];
749                     }
750                     // The decode function expect R, G, B in the channel map
751                     newChannelMap["B"] = info.channelMap["X"];
752                     newChannelMap["G"] = info.channelMap["Y"];
753                     newChannelMap["R"] = info.channelMap["Z"];
754                     info.channelMap = newChannelMap;
755                 }
756                 else {
757                     modelId = RGBAColorModelID.id();
758                     QMap<QString, QString> newChannelMap;
759                     QMap<QString, QString>::iterator it = info.channelMap.begin();
760                     newChannelMap["R"] = it.value();
761                     info.remappedChannels.push_back(ExrPaintLayerInfo::Remap(it.key(), "R"));
762                     ++it;
763                     newChannelMap["G"] = it.value();
764                     info.remappedChannels.push_back(ExrPaintLayerInfo::Remap(it.key(), "G"));
765                     ++it;
766                     newChannelMap["B"] = it.value();
767                     info.remappedChannels.push_back(ExrPaintLayerInfo::Remap(it.key(), "B"));
768                     if (info.channelMap.size() == 4) {
769                         ++it;
770                         newChannelMap["A"] = it.value();
771                         info.remappedChannels.push_back(ExrPaintLayerInfo::Remap(it.key(), "A"));
772                     }
773 
774                     info.channelMap = newChannelMap;
775                 }
776             }
777             else {
778                 dbgFile << info.name << "has" << info.channelMap.size() << "channels, and we don't know what to do.";
779             }
780             if (!modelId.isEmpty()) {
781                 info.colorSpace = kisTypeToColorSpace(modelId, info.imageType);
782             }
783         }
784 
785         // Get colorspace
786         dbgFile << "Image type = " << imageType;
787         const KoColorSpace* colorSpace = kisTypeToColorSpace(RGBAColorModelID.id(), imageType);
788 
789         if (!colorSpace) return ImportExportCodes::FormatColorSpaceUnsupported;
790         dbgFile << "Colorspace: " << colorSpace->name();
791 
792         // Set the colorspace on all groups
793         for (int i = 0; i < groups.size(); ++i) {
794             ExrGroupLayerInfo& info = groups[i];
795             info.colorSpace = colorSpace;
796         }
797 
798         // Create the image
799         //  Make sure the created image is the same size as the displayWindow since
800         //  the dataWindow can be cropped in some cases.
801         int displayWidth = displayWindow.max.x - displayWindow.min.x + 1;
802         int displayHeight = displayWindow.max.y - displayWindow.min.y + 1;
803         d->image = new KisImage(d->doc->createUndoStore(), displayWidth, displayHeight, colorSpace, "");
804 
805         if (!d->image) {
806             return ImportExportCodes::Failure;
807         }
808 
809         /**
810          * EXR semi-transparent images are expected to be rendered on
811          * black to ensure correctness of the light model
812          *
813          * NOTE: We cannot do that automatically, because the EXR may be imported
814          * into the image as a layer, in which case the default color will create
815          * major issues. See https://bugs.kde.org/show_bug.cgi?id=427720
816          */
817         //d->image->setDefaultProjectionColor(KoColor(Qt::black, colorSpace));
818 
819         // Create group layers
820         for (int i = 0; i < groups.size(); ++i) {
821             ExrGroupLayerInfo& info = groups[i];
822             Q_ASSERT(info.parent == 0 || info.parent->groupLayer);
823             KisGroupLayerSP groupLayerParent = (info.parent) ? info.parent->groupLayer : d->image->rootLayer();
824             info.groupLayer = new KisGroupLayer(d->image, info.name, OPACITY_OPAQUE_U8);
825             d->image->addNode(info.groupLayer, groupLayerParent);
826         }
827 
828         // Load the layers
829         for (int i = informationObjects.size() - 1; i >= 0; --i) {
830             ExrPaintLayerInfo& info = informationObjects[i];
831             if (info.colorSpace) {
832                 dbgFile << "Decoding " << info.name << " with " << info.channelMap.size() << " channels, and color space " << info.colorSpace->id();
833                 KisPaintLayerSP layer = new KisPaintLayer(d->image, info.name, OPACITY_OPAQUE_U8, info.colorSpace);
834 
835                 if (!layer) {
836                     return ImportExportCodes::Failure;
837                 }
838 
839                 layer->setCompositeOpId(COMPOSITE_OVER);
840 
841                 switch (info.channelMap.size()) {
842                 case 1:
843                 case 2:
844                     // Decode the data
845                     switch (info.imageType) {
846                     case IT_FLOAT16:
847                         d->decodeData1<half>(file, info, layer, width, dx, dy, height, Imf::HALF);
848                         break;
849                     case IT_FLOAT32:
850                         d->decodeData1<float>(file, info, layer, width, dx, dy, height, Imf::FLOAT);
851                         break;
852                     case IT_UNKNOWN:
853                     case IT_UNSUPPORTED:
854                         qFatal("Impossible error");
855                     }
856                     break;
857                 case 3:
858                 case 4:
859                     // Decode the data
860                     switch (info.imageType) {
861                     case IT_FLOAT16:
862                         d->decodeData4<half>(file, info, layer, width, dx, dy, height, Imf::HALF);
863                         break;
864                     case IT_FLOAT32:
865                         d->decodeData4<float>(file, info, layer, width, dx, dy, height, Imf::FLOAT);
866                         break;
867                     case IT_UNKNOWN:
868                     case IT_UNSUPPORTED:
869                         qFatal("Impossible error");
870                     }
871                     break;
872                 default:
873                     qFatal("Invalid number of channels: %i", info.channelMap.size());
874                 }
875                 // Check if should set the channels
876                 if (!info.remappedChannels.isEmpty()) {
877                     QList<KisMetaData::Value> values;
878                     Q_FOREACH (const ExrPaintLayerInfo::Remap& remap, info.remappedChannels) {
879                         QMap<QString, KisMetaData::Value> map;
880                         map["original"] = KisMetaData::Value(remap.original);
881                         map["current"] = KisMetaData::Value(remap.current);
882                         values.append(map);
883                     }
884                     layer->metaData()->addEntry(KisMetaData::Entry(KisMetaData::SchemaRegistry::instance()->create("http://krita.org/exrchannels/1.0/" , "exrchannels"), "channelsmap", values));
885                 }
886                 // Add the layer
887                 KisGroupLayerSP groupLayerParent = (info.parent) ? info.parent->groupLayer : d->image->rootLayer();
888                 d->image->addNode(layer, groupLayerParent);
889             } else {
890                 dbgFile << "No decoding " << info.name << " with " << info.channelMap.size() << " channels, and lack of a color space";
891             }
892         }
893 
894         // After reading the image, notify the user about changed alpha.
895         if (d->alphaWasModified) {
896             QString msg =
897                     i18nc("@info",
898                           "The image contains pixels with zero alpha channel and non-zero "
899                           "color channels. Krita has modified those pixels to have "
900                           "at least some alpha. The initial values will <i>not</i> "
901                           "be reverted on saving the image back."
902                           "<br/><br/>"
903                           "This will hardly make any visual difference just keep it in mind.");
904             if (d->showNotifications) {
905                 QMessageBox::information(0, i18nc("@title:window", "EXR image has been modified"), msg);
906             } else {
907                 warnKrita << "WARNING:" << msg;
908             }
909         }
910 
911         if (!extraLayersInfo.isNull()) {
912             KisExrLayersSorter sorter(extraLayersInfo, d->image);
913         }
914 
915         return ImportExportCodes::OK;
916 
917     } catch (std::exception &e) {
918         dbgFile << "Error while reading from the exr file: " << e.what();
919 
920         if (!KisImportExportAdditionalChecks::doesFileExist(filename)) {
921             return ImportExportCodes::FileNotExist;
922         } else if(!KisImportExportAdditionalChecks::isFileReadable(filename)) {
923             return ImportExportCodes::NoAccessToRead;
924         } else {
925             return ImportExportCodes::ErrorWhileReading;
926         }
927     }
928 
929     return ImportExportCodes::OK;
930 }
931 
buildImage(const QString & filename)932 KisImportExportErrorCode EXRConverter::buildImage(const QString &filename)
933 {
934     return decode(filename);
935 
936 }
937 
938 
image()939 KisImageSP EXRConverter::image()
940 {
941     return d->image;
942 }
943 
errorMessage() const944 QString EXRConverter::errorMessage() const
945 {
946     return d->errorMessage;
947 }
948 
949 template<typename _T_, int size>
950 struct ExrPixel_ {
951     _T_ data[size];
952 };
953 
954 class Encoder
955 {
956 public:
~Encoder()957     virtual ~Encoder() {}
958     virtual void prepareFrameBuffer(Imf::FrameBuffer*, int line) = 0;
959     virtual void encodeData(int line) = 0;
960 
961 };
962 
963 template<typename _T_, int size, int alphaPos>
964 class EncoderImpl : public Encoder
965 {
966 public:
EncoderImpl(Imf::OutputFile * _file,const ExrPaintLayerSaveInfo * _info,int width)967     EncoderImpl(Imf::OutputFile* _file, const ExrPaintLayerSaveInfo* _info, int width) : file(_file), info(_info), pixels(width), m_width(width) {}
~EncoderImpl()968     ~EncoderImpl() override {}
969     void prepareFrameBuffer(Imf::FrameBuffer*, int line) override;
970     void encodeData(int line) override;
971 private:
972     typedef ExrPixel_<_T_, size> ExrPixel;
973     Imf::OutputFile* file;
974     const ExrPaintLayerSaveInfo* info;
975     QVector<ExrPixel> pixels;
976     int m_width;
977 };
978 
979 template<typename _T_, int size, int alphaPos>
prepareFrameBuffer(Imf::FrameBuffer * frameBuffer,int line)980 void EncoderImpl<_T_, size, alphaPos>::prepareFrameBuffer(Imf::FrameBuffer* frameBuffer, int line)
981 {
982     int xstart = 0;
983     int ystart = 0;
984     ExrPixel* frameBufferData = (pixels.data()) - xstart - (ystart + line) * m_width;
985     for (int k = 0; k < size; ++k) {
986         frameBuffer->insert(info->channels[k].toUtf8(),
987                             Imf::Slice(info->pixelType, (char *) &frameBufferData->data[k],
988                                        sizeof(ExrPixel) * 1,
989                                        sizeof(ExrPixel) * m_width));
990     }
991 }
992 
993 template<typename _T_, int size, int alphaPos>
encodeData(int line)994 void EncoderImpl<_T_, size, alphaPos>::encodeData(int line)
995 {
996     ExrPixel *rgba = pixels.data();
997     KisHLineConstIteratorSP it = info->layerDevice->createHLineConstIteratorNG(0, line, m_width);
998     do {
999         const _T_* dst = reinterpret_cast < const _T_* >(it->oldRawData());
1000 
1001         for (int i = 0; i < size; ++i) {
1002             rgba->data[i] = dst[i];
1003         }
1004 
1005         if (alphaPos != -1) {
1006             multiplyAlpha<_T_, ExrPixel, size, alphaPos>(rgba);
1007         }
1008 
1009         ++rgba;
1010     } while (it->nextPixel());
1011 }
1012 
encoder(Imf::OutputFile & file,const ExrPaintLayerSaveInfo & info,int width)1013 Encoder* encoder(Imf::OutputFile& file, const ExrPaintLayerSaveInfo& info, int width)
1014 {
1015     dbgFile << "Create encoder for" << info.name << info.channels << info.layerDevice->colorSpace()->channelCount();
1016     switch (info.layerDevice->colorSpace()->channelCount()) {
1017     case 1: {
1018         if (info.layerDevice->colorSpace()->colorDepthId() == Float16BitsColorDepthID) {
1019             Q_ASSERT(info.pixelType == Imf::HALF);
1020             return new EncoderImpl < half, 1, -1 > (&file, &info, width);
1021         } else if (info.layerDevice->colorSpace()->colorDepthId() == Float32BitsColorDepthID) {
1022             Q_ASSERT(info.pixelType == Imf::FLOAT);
1023             return new EncoderImpl < float, 1, -1 > (&file, &info, width);
1024         }
1025         break;
1026     }
1027     case 2: {
1028         if (info.layerDevice->colorSpace()->colorDepthId() == Float16BitsColorDepthID) {
1029             Q_ASSERT(info.pixelType == Imf::HALF);
1030             return new EncoderImpl<half, 2, 1>(&file, &info, width);
1031         } else if (info.layerDevice->colorSpace()->colorDepthId() == Float32BitsColorDepthID) {
1032             Q_ASSERT(info.pixelType == Imf::FLOAT);
1033             return new EncoderImpl<float, 2, 1>(&file, &info, width);
1034         }
1035         break;
1036     }
1037     case 4: {
1038         if (info.layerDevice->colorSpace()->colorDepthId() == Float16BitsColorDepthID) {
1039             Q_ASSERT(info.pixelType == Imf::HALF);
1040             return new EncoderImpl<half, 4, 3>(&file, &info, width);
1041         } else if (info.layerDevice->colorSpace()->colorDepthId() == Float32BitsColorDepthID) {
1042             Q_ASSERT(info.pixelType == Imf::FLOAT);
1043             return new EncoderImpl<float, 4, 3>(&file, &info, width);
1044         }
1045         break;
1046     }
1047     default:
1048         qFatal("Impossible error");
1049     }
1050     return 0;
1051 }
1052 
encodeData(Imf::OutputFile & file,const QList<ExrPaintLayerSaveInfo> & informationObjects,int width,int height)1053 void encodeData(Imf::OutputFile& file, const QList<ExrPaintLayerSaveInfo>& informationObjects, int width, int height)
1054 {
1055     QList<Encoder*> encoders;
1056     Q_FOREACH (const ExrPaintLayerSaveInfo& info, informationObjects) {
1057         encoders.push_back(encoder(file, info, width));
1058     }
1059 
1060     for (int y = 0; y < height; ++y) {
1061         Imf::FrameBuffer frameBuffer;
1062         Q_FOREACH (Encoder* encoder, encoders) {
1063             encoder->prepareFrameBuffer(&frameBuffer, y);
1064         }
1065         file.setFrameBuffer(frameBuffer);
1066         Q_FOREACH (Encoder* encoder, encoders) {
1067             encoder->encodeData(y);
1068         }
1069         file.writePixels(1);
1070     }
1071     qDeleteAll(encoders);
1072 }
1073 
wrapLayerDevice(KisPaintDeviceSP device)1074 KisPaintDeviceSP wrapLayerDevice(KisPaintDeviceSP device)
1075 {
1076     const KoColorSpace *cs = device->colorSpace();
1077 
1078     if (cs->colorDepthId() != Float16BitsColorDepthID && cs->colorDepthId() != Float32BitsColorDepthID) {
1079         cs = KoColorSpaceRegistry::instance()->colorSpace(
1080             cs->colorModelId() == GrayAColorModelID ?
1081                 GrayAColorModelID.id() : RGBAColorModelID.id(),
1082             Float16BitsColorDepthID.id());
1083     } else if (cs->colorModelId() != GrayAColorModelID &&
1084                cs->colorModelId() != RGBAColorModelID) {
1085         cs = KoColorSpaceRegistry::instance()->colorSpace(
1086             RGBAColorModelID.id(),
1087             cs->colorDepthId().id());
1088     }
1089 
1090     if (*cs != *device->colorSpace()) {
1091         device = new KisPaintDevice(*device);
1092         device->convertTo(cs);
1093     }
1094 
1095     return device;
1096 }
1097 
buildFile(const QString & filename,KisPaintLayerSP layer)1098 KisImportExportErrorCode EXRConverter::buildFile(const QString &filename, KisPaintLayerSP layer)
1099 {
1100     KIS_ASSERT_RECOVER_RETURN_VALUE(layer, ImportExportCodes::InternalError);
1101 
1102     KisImageSP image = layer->image();
1103     KIS_ASSERT_RECOVER_RETURN_VALUE(image, ImportExportCodes::InternalError);
1104 
1105 
1106     // Make the header
1107     qint32 height = image->height();
1108     qint32 width = image->width();
1109     Imf::Header header(width, height);
1110 
1111     ExrPaintLayerSaveInfo info;
1112     info.layer = layer;
1113     info.layerDevice = wrapLayerDevice(layer->paintDevice());
1114     Imf::PixelType pixelType = Imf::NUM_PIXELTYPES;
1115     if (info.layerDevice->colorSpace()->colorDepthId() == Float16BitsColorDepthID) {
1116         pixelType = Imf::HALF;
1117     }
1118     else if (info.layerDevice->colorSpace()->colorDepthId() == Float32BitsColorDepthID) {
1119         pixelType = Imf::FLOAT;
1120     }
1121     header.channels().insert("R", Imf::Channel(pixelType));
1122     header.channels().insert("G", Imf::Channel(pixelType));
1123     header.channels().insert("B", Imf::Channel(pixelType));
1124     header.channels().insert("A", Imf::Channel(pixelType));
1125 
1126     info.channels.push_back("R");
1127     info.channels.push_back("G");
1128     info.channels.push_back("B");
1129     info.channels.push_back("A");
1130     info.pixelType = pixelType;
1131 
1132     // Open file for writing
1133     try {
1134         Imf::OutputFile file(filename.toUtf8(), header);
1135 
1136         QList<ExrPaintLayerSaveInfo> informationObjects;
1137         informationObjects.push_back(info);
1138         encodeData(file, informationObjects, width, height);
1139         return ImportExportCodes::OK;
1140 
1141     } catch(std::exception &e) {
1142         dbgFile << "Exception while writing to exr file: " << e.what();
1143         if (!KisImportExportAdditionalChecks::isFileWritable(filename)) {
1144             return ImportExportCodes::NoAccessToWrite;
1145         }
1146         return ImportExportCodes::ErrorWhileWriting;
1147     }
1148 
1149 }
1150 
remap(const QMap<QString,QString> & current2original,const QString & current)1151 QString remap(const QMap<QString, QString>& current2original, const QString& current)
1152 {
1153     if (current2original.contains(current)) {
1154         return current2original[current];
1155     }
1156     return current;
1157 }
1158 
makeLayerNamesUnique(QList<ExrPaintLayerSaveInfo> & informationObjects)1159 void EXRConverter::Private::makeLayerNamesUnique(QList<ExrPaintLayerSaveInfo>& informationObjects)
1160 {
1161     typedef QMultiMap<QString, QList<ExrPaintLayerSaveInfo>::iterator> NamesMap;
1162     NamesMap namesMap;
1163 
1164     {
1165         QList<ExrPaintLayerSaveInfo>::iterator it = informationObjects.begin();
1166         QList<ExrPaintLayerSaveInfo>::iterator end = informationObjects.end();
1167 
1168         for (; it != end; ++it) {
1169             namesMap.insert(it->name, it);
1170         }
1171     }
1172 
1173     Q_FOREACH (const QString &key, namesMap.keys()) {
1174         if (namesMap.count(key) > 1) {
1175             KIS_ASSERT_RECOVER(key.endsWith(".")) { continue; }
1176             QString strippedName = key.left(key.size() - 1); // trim the ending dot
1177             int nameCounter = 0;
1178 
1179             NamesMap::iterator it = namesMap.find(key);
1180             NamesMap::iterator end = namesMap.end();
1181 
1182             for (; it != end; ++it) {
1183                 QString newName =
1184                         QString("%1_%2.")
1185                         .arg(strippedName)
1186                         .arg(nameCounter++);
1187 
1188                 it.value()->name = newName;
1189 
1190                 QList<QString>::iterator channelsIt = it.value()->channels.begin();
1191                 QList<QString>::iterator channelsEnd = it.value()->channels.end();
1192 
1193                 for  (; channelsIt != channelsEnd; ++channelsIt) {
1194                     channelsIt->replace(key, newName);
1195                 }
1196             }
1197         }
1198     }
1199 
1200 }
1201 
recBuildPaintLayerSaveInfo(QList<ExrPaintLayerSaveInfo> & informationObjects,const QString & name,KisGroupLayerSP parent)1202 void EXRConverter::Private::recBuildPaintLayerSaveInfo(QList<ExrPaintLayerSaveInfo>& informationObjects, const QString& name, KisGroupLayerSP parent)
1203 {
1204     QSet<KisNodeSP> layersNotSaved;
1205 
1206     for (uint i = 0; i < parent->childCount(); ++i) {
1207         KisNodeSP node = parent->at(i);
1208 
1209         if (KisPaintLayerSP paintLayer = dynamic_cast<KisPaintLayer*>(node.data())) {
1210             QMap<QString, QString> current2original;
1211 
1212             if (paintLayer->metaData()->containsEntry(KisMetaData::SchemaRegistry::instance()->create("http://krita.org/exrchannels/1.0/" , "exrchannels"), "channelsmap")) {
1213 
1214                 const KisMetaData::Entry& entry = paintLayer->metaData()->getEntry(KisMetaData::SchemaRegistry::instance()->create("http://krita.org/exrchannels/1.0/" , "exrchannels"), "channelsmap");
1215                 QList< KisMetaData::Value> values = entry.value().asArray();
1216 
1217                 Q_FOREACH (const KisMetaData::Value& value, values) {
1218                     QMap<QString, KisMetaData::Value> map = value.asStructure();
1219                     if (map.contains("original") && map.contains("current")) {
1220                         current2original[map["current"].toString()] = map["original"].toString();
1221                     }
1222                 }
1223 
1224             }
1225 
1226             ExrPaintLayerSaveInfo info;
1227             info.name = name + paintLayer->name() + '.';
1228             info.layer = paintLayer;
1229             info.layerDevice = wrapLayerDevice(paintLayer->paintDevice());
1230 
1231             if (info.name == QString(HDR_LAYER) + ".") {
1232                 info.channels.push_back("R");
1233                 info.channels.push_back("G");
1234                 info.channels.push_back("B");
1235                 info.channels.push_back("A");
1236             }
1237             else {
1238 
1239                 if (info.layerDevice->colorSpace()->colorModelId() == RGBAColorModelID) {
1240                     info.channels.push_back(info.name + remap(current2original, "R"));
1241                     info.channels.push_back(info.name + remap(current2original, "G"));
1242                     info.channels.push_back(info.name + remap(current2original, "B"));
1243                     info.channels.push_back(info.name + remap(current2original, "A"));
1244                 }
1245                 else if (info.layerDevice->colorSpace()->colorModelId() == GrayAColorModelID) {
1246                     info.channels.push_back(info.name + remap(current2original, "G"));
1247                     info.channels.push_back(info.name + remap(current2original, "A"));
1248                 }
1249                 else if (info.layerDevice->colorSpace()->colorModelId() == GrayColorModelID) {
1250                     info.channels.push_back(info.name + remap(current2original, "G"));
1251                 }
1252                 else if (info.layerDevice->colorSpace()->colorModelId() == XYZAColorModelID) {
1253                     info.channels.push_back(info.name + remap(current2original, "X"));
1254                     info.channels.push_back(info.name + remap(current2original, "Y"));
1255                     info.channels.push_back(info.name + remap(current2original, "Z"));
1256                     info.channels.push_back(info.name + remap(current2original, "A"));
1257                 }
1258 
1259             }
1260 
1261             if (info.layerDevice->colorSpace()->colorDepthId() == Float16BitsColorDepthID) {
1262                 info.pixelType = Imf::HALF;
1263             }
1264             else if (info.layerDevice->colorSpace()->colorDepthId() == Float32BitsColorDepthID) {
1265                 info.pixelType = Imf::FLOAT;
1266             }
1267             else {
1268                 info.pixelType = Imf::NUM_PIXELTYPES;
1269             }
1270 
1271             if (info.pixelType < Imf::NUM_PIXELTYPES) {
1272                 dbgFile << "Going to save layer" << info.name;
1273                 informationObjects.push_back(info);
1274             }
1275             else {
1276                 warnFile << "Will not save layer" << info.name;
1277                 layersNotSaved << node;
1278             }
1279 
1280         }
1281         else if (KisGroupLayerSP groupLayer = dynamic_cast<KisGroupLayer*>(node.data())) {
1282             recBuildPaintLayerSaveInfo(informationObjects, name + groupLayer->name() + '.', groupLayer);
1283         }
1284         else {
1285             /**
1286              * The EXR can store paint and group layers only. The rest will
1287              * go to /dev/null :(
1288              */
1289             layersNotSaved.insert(node);
1290         }
1291     }
1292 
1293     if (!layersNotSaved.isEmpty()) {
1294         reportLayersNotSaved(layersNotSaved);
1295     }
1296 }
1297 
reportLayersNotSaved(const QSet<KisNodeSP> & layersNotSaved)1298 void EXRConverter::Private::reportLayersNotSaved(const QSet<KisNodeSP> &layersNotSaved)
1299 {
1300     QString layersList;
1301     QTextStream textStream(&layersList);
1302     textStream.setCodec("UTF-8");
1303 
1304     Q_FOREACH (KisNodeSP node, layersNotSaved) {
1305         textStream << "<li>" << i18nc("@item:unsupported-node-message", "%1 (type: \"%2\")", node->name(), node->metaObject()->className()) << "</li>";
1306     }
1307 
1308     QString msg =
1309             i18nc("@info",
1310                   "<p>The following layers have a type that is not supported by EXR format:</p>"
1311                   "<r><ul>%1</ul></p>"
1312                   "<p><warning>these layers have <b>not</b> been saved to the final EXR file</warning></p>", layersList);
1313 
1314     errorMessage = msg;
1315 }
1316 
fetchExtraLayersInfo(QList<ExrPaintLayerSaveInfo> & informationObjects)1317 QString EXRConverter::Private::fetchExtraLayersInfo(QList<ExrPaintLayerSaveInfo>& informationObjects)
1318 {
1319     KIS_ASSERT_RECOVER_NOOP(!informationObjects.isEmpty());
1320 
1321     if (informationObjects.size() == 1 && informationObjects[0].name == QString(HDR_LAYER) + ".") {
1322         return QString();
1323     }
1324 
1325     QDomDocument doc("krita-extra-layers-info");
1326     doc.appendChild(doc.createElement("root"));
1327     QDomElement rootElement = doc.documentElement();
1328 
1329     for (int i = 0; i < informationObjects.size(); i++) {
1330         ExrPaintLayerSaveInfo &info = informationObjects[i];
1331         quint32 unused;
1332         KisSaveXmlVisitor visitor(doc, rootElement, unused, QString(), false);
1333         QDomElement el = visitor.savePaintLayerAttributes(info.layer.data(), doc);
1334         // cut the ending '.'
1335         QString strippedName = info.name.left(info.name.size() - 1);
1336 
1337         el.setAttribute(EXR_NAME, strippedName);
1338 
1339         rootElement.appendChild(el);
1340     }
1341 
1342     return doc.toString();
1343 }
1344 
buildFile(const QString & filename,KisGroupLayerSP layer,bool flatten)1345 KisImportExportErrorCode EXRConverter::buildFile(const QString &filename, KisGroupLayerSP layer, bool flatten)
1346 {
1347     KIS_ASSERT_RECOVER_RETURN_VALUE(layer, ImportExportCodes::InternalError);
1348 
1349     KisImageSP image = layer->image();
1350     KIS_ASSERT_RECOVER_RETURN_VALUE(image, ImportExportCodes::InternalError);
1351 
1352     qint32 height = image->height();
1353     qint32 width = image->width();
1354     Imf::Header header(width, height);
1355 
1356     if (flatten) {
1357         KisPaintDeviceSP pd = new KisPaintDevice(*image->projection());
1358         KisPaintLayerSP l = new KisPaintLayer(image, "projection", OPACITY_OPAQUE_U8, pd);
1359         return buildFile(filename, l);
1360     }
1361     else {
1362         QList<ExrPaintLayerSaveInfo> informationObjects;
1363         d->recBuildPaintLayerSaveInfo(informationObjects, "", layer);
1364 
1365         if(informationObjects.isEmpty()) {
1366             return ImportExportCodes::FormatColorSpaceUnsupported;
1367         }
1368         d->makeLayerNamesUnique(informationObjects);
1369 
1370         QByteArray extraLayersInfo = d->fetchExtraLayersInfo(informationObjects).toUtf8();
1371         if (!extraLayersInfo.isNull()) {
1372             header.insert(EXR_KRITA_LAYERS, Imf::StringAttribute(extraLayersInfo.constData()));
1373         }
1374         dbgFile << informationObjects.size() << " layers to save";
1375         Q_FOREACH (const ExrPaintLayerSaveInfo& info, informationObjects) {
1376             if (info.pixelType < Imf::NUM_PIXELTYPES) {
1377                 Q_FOREACH (const QString& channel, info.channels) {
1378                     dbgFile << channel << " " << info.pixelType;
1379                     header.channels().insert(channel.toUtf8().data(), Imf::Channel(info.pixelType));
1380                 }
1381             }
1382         }
1383 
1384         // Open file for writing
1385         try {
1386             Imf::OutputFile file(filename.toUtf8(), header);
1387             encodeData(file, informationObjects, width, height);
1388             return ImportExportCodes::OK;
1389         } catch(std::exception &e) {
1390             dbgFile << "Exception while writing to exr file: " << e.what();
1391             if (!KisImportExportAdditionalChecks::isFileWritable(filename.toUtf8())) {
1392                 return ImportExportCodes::NoAccessToWrite;
1393             }
1394             return ImportExportCodes::ErrorWhileWriting;
1395         }
1396 
1397     }
1398 }
1399 
cancel()1400 void EXRConverter::cancel()
1401 {
1402     warnKrita << "WARNING: Cancelling of an EXR loading is not supported!";
1403 }
1404 
1405 
1406