Fix warnings about unused variables.
[camsim.git] / libcamsim / exporter.cpp
1 /*
2  * Copyright (C) 2017, 2018
3  * Computer Graphics Group, University of Siegen
4  * Written by Martin Lambers <martin.lambers@uni-siegen.de>
5  *
6  * Permission is hereby granted, free of charge, to any person obtaining a copy
7  * of this software and associated documentation files (the "Software"), to deal
8  * in the Software without restriction, including without limitation the rights
9  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10  * copies of the Software, and to permit persons to whom the Software is
11  * furnished to do so, subject to the following conditions:
12  *
13  * The above copyright notice and this permission notice shall be included in
14  * all copies or substantial portions of the Software.
15  *
16  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
19  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22  * SOFTWARE.
23  */
24
25 #include <cstdlib>
26 #include <cstring>
27
28 #include <QFile>
29 #include <QTextStream>
30 #include <QImage>
31 #include <QFuture>
32 #include <QtConcurrent>
33
34 #ifdef HAVE_GTA
35 # include <gta/gta.hpp>
36 #endif
37 #ifdef HAVE_MATIO
38 # include <matio.h>
39 #endif
40 #ifdef HAVE_HDF5
41 # include <H5Cpp.h>
42 #endif
43
44 #include "gl.hpp"
45 #include "exporter.hpp"
46
47
48 namespace CamSim {
49
50 Exporter::Exporter()
51 {
52 }
53
54 bool Exporter::isFileFormatSupported(FileFormat format)
55 {
56     switch (format) {
57     case FileFormat_RAW:
58         return true;
59         break;
60     case FileFormat_CSV:
61         return true;
62         break;
63     case FileFormat_PNM:
64         return true;
65         break;
66     case FileFormat_PNG:
67         return true;
68         break;
69     case FileFormat_PFS:
70         return true;
71         break;
72     case FileFormat_GTA:
73 #ifdef HAVE_GTA
74         return true;
75 #else
76         return false;
77 #endif
78         break;
79     case FileFormat_MAT:
80 #ifdef HAVE_MATIO
81         return true;
82 #else
83         return false;
84 #endif
85         break;
86     case FileFormat_HDF5:
87 #ifdef HAVE_HDF5
88         return true;
89 #else
90         return false;
91 #endif
92         break;
93     case FileFormat_Auto:
94         // cannot happen
95         break;
96     }
97     return false;
98 }
99
100 bool Exporter::isFileFormatCompatible(FileFormat format, const TexData& data, const QList<int>& channels)
101 {
102     int channelCount = (channels.size() == 0 ? data.channels() : channels.size());
103     switch (format) {
104     case FileFormat_RAW:
105         return true;
106         break;
107     case FileFormat_CSV:
108         return true;
109         break;
110     case FileFormat_PNM:
111         return (channelCount == 1 || channelCount == 3) && data.type() == GL_UNSIGNED_BYTE;
112         break;
113     case FileFormat_PNG:
114         return (channelCount == 1 || channelCount == 3) && data.type() == GL_UNSIGNED_BYTE;
115         break;
116     case FileFormat_PFS:
117         return true;
118         break;
119     case FileFormat_GTA:
120         return true;
121         break;
122     case FileFormat_MAT:
123         return true;
124         break;
125     case FileFormat_HDF5:
126         return true;
127         break;
128     case FileFormat_Auto:
129         // cannot happen
130         break;
131     }
132     return false;
133 }
134
135 FileFormat Exporter::fileFormatFromName(const QString& fileName)
136 {
137     if (fileName.endsWith(".raw"))
138         return FileFormat_RAW;
139     else if (fileName.endsWith(".csv"))
140         return FileFormat_CSV;
141     else if (fileName.endsWith(".pgm") || fileName.endsWith(".ppm"))
142         return FileFormat_PNM;
143     else if (fileName.endsWith(".png"))
144         return FileFormat_PNG;
145     else if (fileName.endsWith(".pfs"))
146         return FileFormat_PFS;
147     else if (fileName.endsWith(".gta"))
148         return FileFormat_GTA;
149     else if (fileName.endsWith(".mat"))
150         return FileFormat_MAT;
151     else if (fileName.endsWith(".h5"))
152         return FileFormat_HDF5;
153     else
154         return FileFormat_Auto;
155 }
156
157 bool Exporter::checkExportRequest(Exporter* asyncExporter,
158         const QString& fileName, FileFormat format,
159         const QList<TexData>& dataList, const QList<QList<int>>& channelsList,
160         int compressionLevel,
161         FileFormat& cleanedFormat,
162         QList<QList<int>>& cleanedChannelsList,
163         int& cleanedCompressionLevel)
164 {
165     if (dataList.size() == 0) {
166         qCritical("%s: no data to export", qPrintable(fileName));
167         return false;
168     }
169     cleanedFormat = format;
170     if (format == FileFormat_Auto) {
171         cleanedFormat = fileFormatFromName(fileName);
172     }
173     if (cleanedFormat == FileFormat_Auto) {
174         qCritical("%s: cannot detect file format from name", qPrintable(fileName));
175         return false;
176     }
177     if (!isFileFormatSupported(cleanedFormat)) {
178         qCritical("%s: file format not supported (library missing)", qPrintable(fileName));
179         return false;
180     }
181     if (asyncExporter) {
182         if (asyncExporter->_asyncExportFileNames.contains(fileName)) {
183             qCritical("%s: cannot have more than one async export per file", qPrintable(fileName));
184             return false;
185         }
186     }
187     if (channelsList.size() != 0 && channelsList.size() != dataList.size()) {
188         qCritical("%s: number of channel lists does not match number of data", qPrintable(fileName));
189         return false;
190     }
191     for (int i = 0; i < channelsList.size(); i++) {
192         for (int j = 0; j < channelsList[i].size(); j++) {
193             if (channelsList[i][j] < 0 || channelsList[i][j] >= dataList[i].channels()) {
194                 qCritical("%s: invalid channel given", qPrintable(fileName));
195                 return false;
196             }
197         }
198     }
199     cleanedChannelsList.clear();
200     if (channelsList.size() == 0) {
201         for (int i = 0; i < dataList.size(); i++) {
202             QList<int> cleanChannels;
203             for (int j = 0; j < dataList[i].channels(); j++)
204                 cleanChannels.append(j);
205             cleanedChannelsList.append(cleanChannels);
206         }
207     } else {
208         for (int i = 0; i < dataList.size(); i++) {
209             if (channelsList[i].size() == 0) {
210                 QList<int> cleanChannels;
211                 for (int j = 0; j < dataList[i].channels(); j++)
212                     cleanChannels.append(j);
213                 cleanedChannelsList.append(cleanChannels);
214             } else {
215                 cleanedChannelsList.append(channelsList[i]);
216             }
217         }
218     }
219     for (int i = 0; i < dataList.size(); i++) {
220         if (!isFileFormatCompatible(cleanedFormat, dataList[i], cleanedChannelsList[i])
221                 || (i > 0 && cleanedFormat == FileFormat_PNG)) {
222             qCritical("%s: file format is not compatible with data", qPrintable(fileName));
223             return false;
224         }
225     }
226     cleanedCompressionLevel = compressionLevel;
227     if (cleanedCompressionLevel < 0)
228         cleanedCompressionLevel = 0;
229     else if (cleanedCompressionLevel > 9)
230         cleanedCompressionLevel = 9;
231     return true;
232 }
233
234 bool Exporter::exportData(Exporter* asyncExporter,
235         const QString& fileName, FileFormat format,
236         const QList<TexData>& dataList,
237         const QList<QList<int>>& channelsList,
238         int compressionLevel)
239 {
240     FileFormat cleanedFormat;
241     QList<QList<int>> cleanedChannelsList;
242     int cleanedCompressionLevel;
243     if (!checkExportRequest(asyncExporter, fileName, format,
244                 dataList, channelsList, compressionLevel,
245                 cleanedFormat, cleanedChannelsList, cleanedCompressionLevel)) {
246         return false;
247     }
248     if (asyncExporter) {
249         asyncExporter->_asyncExportFileNames.append(fileName);
250         QFuture<bool> exp;
251         switch (cleanedFormat) {
252         case FileFormat_RAW:
253             exp = QtConcurrent::run(writeRAW, fileName, dataList, cleanedChannelsList, cleanedCompressionLevel);
254             break;
255         case FileFormat_CSV:
256             exp = QtConcurrent::run(writeCSV, fileName, dataList, cleanedChannelsList, cleanedCompressionLevel);
257             break;
258         case FileFormat_PNM:
259             exp = QtConcurrent::run(writePNM, fileName, dataList, cleanedChannelsList, cleanedCompressionLevel);
260             break;
261         case FileFormat_PNG:
262             exp = QtConcurrent::run(writePNG, fileName, dataList, cleanedChannelsList, cleanedCompressionLevel);
263             break;
264         case FileFormat_PFS:
265             exp = QtConcurrent::run(writePFS, fileName, dataList, cleanedChannelsList, cleanedCompressionLevel);
266             break;
267         case FileFormat_GTA:
268             exp = QtConcurrent::run(writeGTA, fileName, dataList, cleanedChannelsList, cleanedCompressionLevel);
269             break;
270         case FileFormat_MAT:
271             exp = QtConcurrent::run(writeMAT, fileName, dataList, cleanedChannelsList, cleanedCompressionLevel);
272             break;
273         case FileFormat_HDF5:
274             exp = QtConcurrent::run(writeHDF, fileName, dataList, cleanedChannelsList, cleanedCompressionLevel);
275             break;
276         case FileFormat_Auto:
277             // cannot happen
278             break;
279         }
280         asyncExporter->_asyncExports.append(exp);
281         return true;
282     } else {
283         switch (cleanedFormat) {
284         case FileFormat_RAW:
285             return writeRAW(fileName, dataList, cleanedChannelsList, cleanedCompressionLevel);
286             break;
287         case FileFormat_CSV:
288             return writeCSV(fileName, dataList, cleanedChannelsList, cleanedCompressionLevel);
289             break;
290         case FileFormat_PNM:
291             return writePNM(fileName, dataList, cleanedChannelsList, cleanedCompressionLevel);
292             break;
293         case FileFormat_PNG:
294             return writePNG(fileName, dataList, cleanedChannelsList, cleanedCompressionLevel);
295             break;
296         case FileFormat_PFS:
297             return writePFS(fileName, dataList, cleanedChannelsList, cleanedCompressionLevel);
298             break;
299         case FileFormat_GTA:
300             return writeGTA(fileName, dataList, cleanedChannelsList, cleanedCompressionLevel);
301             break;
302         case FileFormat_MAT:
303             return writeMAT(fileName, dataList, cleanedChannelsList, cleanedCompressionLevel);
304             break;
305         case FileFormat_HDF5:
306             return writeHDF(fileName, dataList, cleanedChannelsList, cleanedCompressionLevel);
307             break;
308         case FileFormat_Auto:
309             // cannot happen
310             break;
311         }
312         return false;
313     }
314 }
315
316 bool Exporter::waitForAsyncExports()
317 {
318     bool ok = true;
319     for (int i = 0; i < _asyncExports.size(); i++) {
320         bool asyncOk = _asyncExports[i].result();
321         if (!asyncOk)
322             ok = false;
323     }
324     _asyncExports.clear();
325     _asyncExportFileNames.clear();
326     return ok;
327 }
328
329 static bool haveDefaultChannels(const TexData& data, const QList<int>& channels)
330 {
331     bool defaultChannels = true;
332     if (channels.size() != data.channels())
333         defaultChannels = false;
334     for (int i = 0; i < channels.size(); i++)
335         if (channels[i] != i)
336             defaultChannels = false;
337     return defaultChannels;
338 }
339
340 bool Exporter::writeRAW(const QString fileName, const QList<TexData> dataList, const QList<QList<int>> channelsList, int /* compressionLevel */)
341 {
342     QFile file(fileName);
343     QFile header(fileName + "_header");
344     if (!file.open(QIODevice::Append) || !header.open(QIODevice::Append)) {
345         qCritical("%s: cannot open for writing", qPrintable(fileName));
346         return false;
347     }
348     for (int i = 0; i < dataList.size(); i++) {
349         const TexData& data = dataList[i];
350         const QList<int>& channels = channelsList[i];
351         if (haveDefaultChannels(data, channels)) {
352             unsigned int n = file.write(static_cast<const char*>(data.packedData()), data.packedDataSize());
353             if (n != data.packedDataSize()) {
354                 qCritical("%s: write error", qPrintable(fileName));
355                 return false;
356             }
357         } else {
358             for (int y = 0; y < data.height(); y++) {
359                 for (int x = 0; x < data.width(); x++) {
360                     for (int c = 0; c < channels.size(); c++) {
361                         unsigned int n = file.write(static_cast<const char*>(data.element(x, y, channels[c])), data.typeSize());
362                         if (n != data.typeSize()) {
363                             qCritical("%s: write error", qPrintable(fileName));
364                             return false;
365                         }
366                     }
367                 }
368             }
369         }
370         QTextStream headerStream(&header);
371         headerStream << "dimensions: " << data.width() << " " << data.height() << '\n';
372         QString typeString = (data.type() == GL_UNSIGNED_BYTE ? "uint8" : data.type() == GL_UNSIGNED_INT ? "uint32" : "float32");
373         headerStream << "components:";
374         for (int i = 0; i < channels.size(); i++)
375             headerStream << ' ' << typeString;
376         headerStream << '\n';
377         headerStream << "component names:";
378         for (int i = 0; i < channels.size(); i++) {
379             QString channelName = (data.channelName(i).isEmpty() ? "unnamed" : data.channelName(i));
380             headerStream << ' ' << channelName;
381         }
382         headerStream << '\n';
383     }
384     if (!file.flush() || !header.flush()) {
385         qCritical("%s: write error", qPrintable(fileName));
386         return false;
387     }
388     file.close();
389     header.close();
390     return true;
391 }
392
393 bool Exporter::writeCSV(const QString fileName, const QList<TexData> dataList, const QList<QList<int>> channelsList, int /* compressionLevel */)
394 {
395     QFile file(fileName);
396     if (!file.open(QIODevice::WriteOnly)) {
397         qCritical("%s: cannot open for writing", qPrintable(fileName));
398         return false;
399     }
400     for (int i = 0; i < dataList.size(); i++) {
401         const TexData& data = dataList[i];
402         const QList<int>& channels = channelsList[i];
403         for (int c = 0; c < channels.size(); c++) {
404             for (int y = 0; y < data.height(); y++) {
405                 for (int x = 0; x < data.width(); x++) {
406                     QString element;
407                     const void* p = data.element(x, y, channels[c]);
408                     switch (data.type()) {
409                     case GL_UNSIGNED_BYTE:
410                         element.append(QString::number(*(static_cast<const unsigned char*>(p))));
411                         break;
412                     case GL_UNSIGNED_INT:
413                         element.append(QString::number(*(static_cast<const unsigned int*>(p))));
414                         break;
415                     case GL_FLOAT:
416                         element.append(QString::number(*(static_cast<const unsigned int*>(p)), 'g', 9));
417                         break;
418                     }
419                     if (x != data.width() - 1)
420                         element.append(',');
421                     file.write(qPrintable(element));
422                 }
423                 file.write("\n");
424             }
425             file.write("\n");
426         }
427     }
428     if (!file.flush()) {
429         qCritical("%s: write error", qPrintable(fileName));
430         return false;
431     }
432     file.close();
433     return true;
434 }
435
436 bool Exporter::writePNM(const QString fileName, const QList<TexData> dataList, const QList<QList<int>> channelsList, int /* compressionLevel */)
437 {
438     FILE* f = std::fopen(qPrintable(fileName), "ab");
439     if (!f) {
440         qCritical("%s: cannot open for writing", qPrintable(fileName));
441         return false;
442     }
443     for (int i = 0; i < dataList.size(); i++) {
444         const TexData& data = dataList[i];
445         const QList<int>& channels = channelsList[i];
446         const unsigned char* imgDataPtr;
447         QVector<unsigned char> imgData;
448         imgDataPtr = static_cast<const unsigned char*>(data.packedData());
449         std::fprintf(f, "P%d\n%d %d\n255\n", channels.size() == 1 ? 5 : 6,
450                 data.width(), data.height());
451         std::fwrite(imgDataPtr, data.width() * data.height() * data.channels(), 1, f);
452     }
453     if (std::fflush(f) != 0 || std::fclose(f) != 0) {
454         qCritical("%s: write error", qPrintable(fileName));
455         return false;
456     }
457     return true;
458 }
459
460 bool Exporter::writePNG(const QString fileName, const QList<TexData> dataList, const QList<QList<int>> channelsList, int compressionLevel)
461 {
462     const TexData& data = dataList[0];
463     const QList<int>& channels = channelsList[0];
464     if (data.type() == GL_UNSIGNED_BYTE && data.packedLineSize() % 4 == 0) {
465         /* fast path for common case */
466         QImage img(static_cast<const uchar*>(data.packedData()), data.width(), data.height(),
467                 channels.size() == 1 ? QImage::Format_Grayscale8 : QImage::Format_RGB888);
468         return img.save(fileName, "PNG", 11 * (9 - compressionLevel) + 1);
469     } else {
470         QImage img(data.width(), data.height(),
471                 channels.size() == 1 ? QImage::Format_Grayscale8 : QImage::Format_RGB888);
472         for (int y = 0; y < data.height(); y++) {
473             unsigned char* scanline = img.scanLine(y);
474             for (int x = 0; x < data.width(); x++) {
475                 for (int c = 0; c < channels.size(); c++) {
476                     const void* p = data.element(x, y, channels[c]);
477                     unsigned char byte = *(static_cast<const unsigned char*>(p));
478                     std::memcpy(scanline + x * channels.size() + c, &byte, 1);
479                 }
480             }
481         }
482         return img.save(fileName, "PNG", 11 * (9 - compressionLevel) + 1);
483     }
484 }
485
486 bool Exporter::writePFS(const QString fileName, const QList<TexData> dataList, const QList<QList<int>> channelsList, int /* compressionLevel */)
487 {
488     FILE* f = std::fopen(qPrintable(fileName), "ab");
489     if (!f) {
490         qCritical("%s: cannot open for writing", qPrintable(fileName));
491         return false;
492     }
493     for (int i = 0; i < dataList.size(); i++) {
494         const TexData& data = dataList[i];
495         const QList<int>& channels = channelsList[i];
496         std::fprintf(f, "PFS1\n%d %d\n%d\n0\n", data.width(), data.height(), channels.size());
497         for (int c = 0; c < channels.size(); c++) {
498             QString channelName = data.channelName(channels[c]);
499             if (channelName.isEmpty()) {
500                 char defaultChannelName[] = "CAMSIM-0";
501                 defaultChannelName[7] += c;
502                 channelName = defaultChannelName;
503             }
504             std::fprintf(f, "%s\n0\n", qPrintable(channelName));
505         }
506         std::fprintf(f, "ENDH");
507         QByteArray channelData;
508         for (int c = 0; c < channels.size(); c++) {
509             if (data.type() == GL_FLOAT) {
510                 channelData = data.planarDataArray(channels[c]);
511                 std::fwrite(channelData.constData(), data.planarDataSize(), 1, f);
512             } else {
513                 channelData.resize(data.width() * data.height() * sizeof(float));
514                 for (int y = 0; y < data.height(); y++) {
515                     for (int x = 0; x < data.width(); x++) {
516                         float* dst = reinterpret_cast<float*>(channelData.data()) + y * data.width() + x;
517                         const void* src = data.element(x, y, channels[c]);
518                         if (data.type() == GL_UNSIGNED_BYTE)
519                             *dst = *(static_cast<const unsigned char*>(src));
520                         else if (data.type() == GL_UNSIGNED_INT)
521                             *dst = *(static_cast<const unsigned int*>(src));
522                     }
523                 }
524                 std::fwrite(channelData.constData(), channelData.size(), 1, f);
525             }
526         }
527     }
528     if (std::fflush(f) != 0 || std::fclose(f) != 0) {
529         qCritical("%s: write error", qPrintable(fileName));
530         return false;
531     }
532     return true;
533 }
534
535 bool Exporter::writeGTA(const QString fileName, const QList<TexData> dataList, const QList<QList<int>> channelsList, int compressionLevel)
536 {
537 #ifdef HAVE_GTA
538     FILE* f = std::fopen(qPrintable(fileName), "ab");
539     if (!f) {
540         qCritical("%s: cannot open for writing", qPrintable(fileName));
541         return false;
542     }
543     try {
544         for (int i = 0; i < dataList.size(); i++) {
545             const TexData& data = dataList[i];
546             const QList<int>& channels = channelsList[i];
547             gta::header hdr;
548             switch (compressionLevel) {
549             case 0:
550             default:
551                 break;
552             case 1:
553                 hdr.set_compression(gta::zlib1);
554                 break;
555             case 2:
556                 hdr.set_compression(gta::zlib2);
557                 break;
558             case 3:
559                 hdr.set_compression(gta::zlib3);
560                 break;
561             case 4:
562                 hdr.set_compression(gta::zlib4);
563                 break;
564             case 5:
565                 hdr.set_compression(gta::zlib5);
566                 break;
567             case 6:
568                 hdr.set_compression(gta::zlib6);
569                 break;
570             case 7:
571                 hdr.set_compression(gta::zlib7);
572                 break;
573             case 8:
574                 hdr.set_compression(gta::zlib8);
575                 break;
576             case 9:
577                 hdr.set_compression(gta::zlib9);
578                 break;
579             }
580             hdr.set_dimensions(data.width(), data.height());
581             gta::type componentType;
582             switch (data.type()) {
583             case GL_UNSIGNED_BYTE:
584                 componentType = gta::uint8;
585                 break;
586             case GL_UNSIGNED_INT:
587                 componentType = gta::uint32;
588                 break;
589             case GL_FLOAT:
590             default:
591                 componentType = gta::float32;
592                 break;
593             }
594             if (haveDefaultChannels(data, channels)) {
595                 std::vector<gta::type> types(data.channels(), componentType);
596                 hdr.set_components(data.channels(), &(types[0]));
597                 for (int c = 0; c < data.channels(); c++) {
598                     if (!data.channelName(c).isEmpty())
599                         hdr.component_taglist(c).set("INTERPRETATION", qPrintable(data.channelName(c)));
600                 }
601                 hdr.write_to(f);
602                 hdr.write_data(f, data.packedData());
603             } else {
604                 std::vector<gta::type> types(channels.size(), componentType);
605                 hdr.set_components(channels.size(), &(types[0]));
606                 for (int c = 0; c < channels.size(); c++) {
607                     if (!data.channelName(channels[c]).isEmpty())
608                         hdr.component_taglist(c).set("INTERPRETATION", qPrintable(data.channelName(channels[c])));
609                 }
610                 hdr.write_to(f);
611                 gta::io_state iostate;
612                 for (int y = 0; y < data.height(); y++) {
613                     for (int x = 0; x < data.width(); x++) {
614                         unsigned char element[hdr.element_size()];
615                         for (int c = 0; c < channels.size(); c++) {
616                             int channel = channels[c];
617                             std::memcpy(static_cast<void*>(element + c * data.typeSize()),
618                                     data.element(x, y, channel),
619                                     data.typeSize());
620                         }
621                         hdr.write_elements(iostate, f, 1, static_cast<void*>(element));
622                     }
623                 }
624             }
625         }
626     }
627     catch (std::exception& e) {
628         qCritical("%s: %s", qPrintable(fileName), e.what());
629         return false;
630     }
631     if (std::fflush(f) != 0 || std::fclose(f) != 0) {
632         qCritical("%s: write error", qPrintable(fileName));
633         return false;
634     }
635     return true;
636 #else
637     Q_UNUSED(fileName);
638     Q_UNUSED(dataList);
639     Q_UNUSED(channelsList);
640     Q_UNUSED(compressionLevel);
641     return false;
642 #endif
643 }
644
645 bool Exporter::writeMAT(const QString fileName, const QList<TexData> dataList, const QList<QList<int>> channelsList, int compressionLevel)
646 {
647 #ifdef HAVE_MATIO
648     // delete an existing file because MATIO refuses to overwrite it
649     QFile file(fileName); file.remove();
650     mat_t *mat = Mat_Create(qPrintable(fileName), NULL);
651     if (!mat) {
652         qCritical("%s: cannot open for writing", qPrintable(fileName));
653         return false;
654     }
655     int defaultNameCounter = 0;
656     for (int i = 0; i < dataList.size(); i++) {
657         const TexData& data = dataList[i];
658         const QList<int>& channels = channelsList[i];
659         enum matio_classes classType;
660         enum matio_types dataType;
661         switch (data.type()) {
662         case GL_UNSIGNED_BYTE:
663             classType = MAT_C_UINT8;
664             dataType = MAT_T_UINT8;
665             break;
666         case GL_UNSIGNED_INT:
667             classType = MAT_C_UINT32;
668             dataType = MAT_T_UINT32;
669             break;
670         case GL_FLOAT:
671         default:
672             classType = MAT_C_SINGLE;
673             dataType = MAT_T_SINGLE;
674             break;
675         }
676         for (int c = 0; c < channels.size(); c++) {
677             QString varName = data.channelName(channels[c]);
678             if (varName.isEmpty())
679                 varName = QString("CAMSIM") + QString::number(defaultNameCounter++);
680             size_t dims[2] = { static_cast<size_t>(data.height()), static_cast<size_t>(data.width()) };
681             QByteArray transposedPlanarDataArray = data.transposedPlanarDataArray(channels[c]);
682             matvar_t *matvar = Mat_VarCreate(qPrintable(varName), classType, dataType, 2, &(dims[0]),
683                     transposedPlanarDataArray.data(), MAT_F_DONT_COPY_DATA);
684             if (!matvar) {
685                 qCritical("%s: cannot create variable", qPrintable(fileName));
686                 Mat_Close(mat);
687                 return false;
688             }
689             if (Mat_VarWrite(mat, matvar, compressionLevel > 0 ? MAT_COMPRESSION_ZLIB : MAT_COMPRESSION_NONE) != 0) {
690                 qCritical("%s: cannot write variable", qPrintable(fileName));
691                 Mat_Close(mat);
692                 return false;
693             }
694             Mat_VarFree(matvar);
695         }
696     }
697     Mat_Close(mat);
698     return true;
699 #else
700     Q_UNUSED(fileName);
701     Q_UNUSED(dataList);
702     Q_UNUSED(channelsList);
703     Q_UNUSED(compressionLevel);
704     return false;
705 #endif
706 }
707
708 bool Exporter::writeHDF(const QString fileName, const QList<TexData> dataList, const QList<QList<int>> channelsList, int compressionLevel)
709 {
710 #ifdef HAVE_HDF5
711     int defaultNameCounter = 0;
712     try {
713         H5::Exception::dontPrint();
714         H5::H5File file(qPrintable(fileName), H5F_ACC_TRUNC);
715         H5::FloatType floattype(H5::PredType::NATIVE_FLOAT);
716         H5::IntType uchartype(H5::PredType::NATIVE_UCHAR);
717         H5::IntType uinttype(H5::PredType::NATIVE_UINT);
718         for (int i = 0; i < dataList.size(); i++) {
719             const TexData& data = dataList[i];
720             const QList<int>& channels = channelsList[i];
721             for (int c = 0; c < channels.size(); c++) {
722                 QString varName = data.channelName(channels[c]);
723                 if (varName.isEmpty())
724                     varName = QString("CAMSIM") + QString::number(defaultNameCounter++);
725                 QByteArray transposedPlanarDataArray = data.transposedPlanarDataArray(channels[c]);
726                 hsize_t dims[2] = { static_cast<hsize_t>(data.width()), static_cast<hsize_t>(data.height()) };
727                 H5::DataSpace dataspace(2, dims);
728                 H5::DSetCreatPropList proplist;
729                 if (compressionLevel > 0)
730                     proplist.setDeflate(compressionLevel);
731                 H5::DataSet dataset;
732                 switch (data.type()) {
733                 case GL_UNSIGNED_BYTE:
734                     dataset = file.createDataSet(qPrintable(varName), uchartype, dataspace, proplist);
735                     dataset.write(transposedPlanarDataArray.constData(), H5::PredType::NATIVE_UCHAR);
736                     break;
737                 case GL_UNSIGNED_INT:
738                     dataset = file.createDataSet(qPrintable(varName), uinttype, dataspace, proplist);
739                     dataset.write(transposedPlanarDataArray.constData(), H5::PredType::NATIVE_UINT);
740                     break;
741                 case GL_FLOAT:
742                 default:
743                     dataset = file.createDataSet(qPrintable(varName), floattype, dataspace, proplist);
744                     dataset.write(transposedPlanarDataArray.constData(), H5::PredType::NATIVE_FLOAT);
745                     break;
746                 }
747             }
748         }
749     }
750     catch (H5::Exception& error) {
751         qCritical("%s: %s", qPrintable(fileName), error.getCDetailMsg());
752         return false;
753     }
754     return true;
755 #else
756     Q_UNUSED(fileName);
757     Q_UNUSED(dataList);
758     Q_UNUSED(channelsList);
759     Q_UNUSED(compressionLevel);
760     return false;
761 #endif
762 }
763
764 }