1 //
2 // SPDX-License-Identifier: BSD-3-Clause
3 // Copyright (c) Contributors to the OpenEXR Project.
4 //
5 
6 #ifdef NDEBUG
7 #    undef NDEBUG
8 #endif
9 
10 #include "testDeepScanLineBasic.h"
11 #include "random.h"
12 
13 #include "ImfDeepScanLineInputFile.h"
14 #include "ImfDeepScanLineOutputFile.h"
15 #include "ImfDeepFrameBuffer.h"
16 #include "ImfPartType.h"
17 #include "ImfChannelList.h"
18 #include "ImfArray.h"
19 #include "IlmThreadPool.h"
20 
21 #include "tmpDir.h"
22 
23 #include <assert.h>
24 #include <string.h>
25 #include <stdlib.h>
26 #include <stdio.h>
27 
28 #include <vector>
29 
30 namespace IMF = OPENEXR_IMF_NAMESPACE;
31 using namespace IMF;
32 using namespace std;
33 using namespace IMATH_NAMESPACE;
34 using namespace ILMTHREAD_NAMESPACE;
35 
36 namespace
37 {
38 
39 const int width = 8193;
40 const int height = 1;
41 const int minX = 0;
42 const int minY = 0;
43 const long numGib = 1; // number of GiB to allocate for huge test
44 const Box2i dataWindow(V2i(minX, minY), V2i(minX + width - 1, minY + height - 1));
45 const Box2i displayWindow(V2i(0, 0), V2i(minX + width * 2, minY + height * 2));
46 
47 vector<int> channelTypes;
48 Array2D<unsigned int> sampleCount;
49 vector<unsigned char> storage; // actual pixel storage for entire image (effectively)
50 
51 Header header;
52 
53 void
generateRandomFile(int channelCount,Compression compression,bool random_channel_data,const std::string & fn)54 generateRandomFile (int channelCount,
55                     Compression compression,
56                     bool random_channel_data,
57                     const std::string & fn)
58 {
59     cout << "generating ... " << flush;
60     header = Header(displayWindow, dataWindow,
61                     1,
62                     IMATH_NAMESPACE::V2f (0, 0),
63                     1,
64                     INCREASING_Y,
65                     compression);
66 
67     cout << "compression " << compression << " " << flush;
68 
69     //
70     // Add channels.
71     //
72 
73     channelTypes.clear();
74 
75     for (int i = 0; i < channelCount; i++)
76     {
77         int type = random_int(3);
78         stringstream ss;
79         ss << i;
80         string str = ss.str();
81         if (type == 0)
82             header.channels().insert(str, Channel(IMF::UINT));
83         if (type == 1)
84             header.channels().insert(str, Channel(IMF::HALF));
85         if (type == 2)
86             header.channels().insert(str, Channel(IMF::FLOAT));
87         channelTypes.push_back(type);
88     }
89 
90     header.setType(DEEPSCANLINE);
91 
92     Array<Array2D< void* > > data(channelCount);
93     for (int i = 0; i < channelCount; i++)
94         data[i].resizeErase(height, width);
95 
96     sampleCount.resizeErase(height, width);
97 
98     remove (fn.c_str());
99     DeepScanLineOutputFile file (fn.c_str(), header, 8);
100 
101     DeepFrameBuffer frameBuffer;
102 
103     frameBuffer.insertSampleCountSlice (Slice (IMF::UINT,                    // type // 7
104                                         (char *) (&sampleCount[0][0]
105                                                   - dataWindow.min.x
106                                                   - dataWindow.min.y * width),               // base // 8
107                                         sizeof (unsigned int) * 1,          // xStride// 9
108                                         sizeof (unsigned int) * width));    // yStride// 10
109 
110 
111     // count total size of all pixels
112     uint64_t bytes_per_sample = 0;
113     for (int i = 0; i < channelCount; i++)
114     {
115         PixelType type = NUM_PIXELTYPES;
116         if (channelTypes[i] == 0)
117             type = IMF::UINT;
118         if (channelTypes[i] == 1)
119             type = IMF::HALF;
120         if (channelTypes[i] == 2)
121             type = IMF::FLOAT;
122 
123         stringstream ss;
124         ss << i;
125         string str = ss.str();
126 
127         int sampleSize = 0;
128         if (channelTypes[i] == 0) sampleSize = sizeof (unsigned int);
129         if (channelTypes[i] == 1) sampleSize = sizeof (half);
130         if (channelTypes[i] == 2) sampleSize = sizeof (float);
131 
132         int pointerSize = sizeof(char *);
133 
134          bytes_per_sample+=sampleSize;
135 
136         frameBuffer.insert (str,                            // name // 6
137                             DeepSlice (type,                    // type // 7
138                             (char *) (&data[i][0][0]
139                                       - dataWindow.min.x
140                                       - dataWindow.min.y * width),               // base // 8
141                             pointerSize * 1,          // xStride// 9
142                             pointerSize * width,      // yStride// 10
143                             sampleSize));             // sampleStride
144     }
145 
146     file.setFrameBuffer(frameBuffer);
147 
148     cout << "writing file " << endl;
149 
150     uint64_t total_number_of_samples = 0;
151 
152     // compute ideal number of samples per pixel assuming we want abotut 5GiB of data
153     // int samples_per_pixel = int(5l*1024l*1024l*1024l/uint64_t(width*height)) / bytes_per_sample;
154 
155     // compute ideal number of samples per pixel assuming we want abotut 15GiB of data
156     int samples_per_pixel = int(numGib*1024l*1024l*1024l/uint64_t(width*height)) / bytes_per_sample;
157 
158     cout << "  generating approx. " << samples_per_pixel << " samples per pixel\n";
159 
160     for (int i = 0; i < height; i++)
161     {
162             for (int j = 0; j < width; j++)
163             {
164                 sampleCount[i][j] = random_int(4000) + (samples_per_pixel-2000);
165                 total_number_of_samples += sampleCount[i][j];
166             }
167     }
168 
169     cout << "  total number of samples: " << total_number_of_samples << std::endl;
170     cout << "  storage required: " << total_number_of_samples*bytes_per_sample << " bytes (" <<
171     ((total_number_of_samples*bytes_per_sample)>>30) << "GiB)" <<  std::endl;
172 
173 
174     //
175     // storage layout scheme:
176     // [Pixel1: [Channel1: [Sample1 Sample2 Sample...] ] [Channel2: [Sample1 Sample2...] ] [Channnel...] ]
177     // [Pixel2: [Channel1: [Sample1 Sample2 Sample...] ] [Channel2: [Sample1 Sample2...] ] [Channnel...] ]
178     // [Pixel...]
179     //
180     storage.resize(total_number_of_samples*bytes_per_sample);
181 
182 
183 
184     uint64_t write_pointer=0;
185 
186     for (int i = 0; i < height; i++)
187     {
188         //
189         // Fill in data at the last minute.
190         //
191 
192 
193         for (int j = 0; j < width; j++)
194         {
195                 for (int k = 0; k < channelCount; k++)
196                 {
197                     data[k][i][j]=&storage[write_pointer];
198                     if (channelTypes[k] == 0)
199                         write_pointer+=sizeof(int)*sampleCount[i][j];
200                     if (channelTypes[k] == 1)
201                         write_pointer+=sizeof(half)*sampleCount[i][j];
202                     if (channelTypes[k] == 2)
203                         write_pointer+=sizeof(float)*sampleCount[i][j];
204 
205                     if(random_channel_data)
206                     {
207                         for (unsigned int l = 0; l < sampleCount[i][j]; l++)
208                         {
209                             if (channelTypes[k] == 0)
210                                 ((unsigned int*)data[k][i][j])[l] = random_int();
211                             if (channelTypes[k] == 1)
212                                 ((half*)data[k][i][j])[l] = random_float();
213                             if (channelTypes[k] == 2)
214                                 ((float*)data[k][i][j])[l] = random_float();
215                         }
216                     }
217                     else
218                     {
219                       for (unsigned int l = 0; l < sampleCount[i][j]; l++)
220                       {
221                           if (channelTypes[k] == 0)
222                               ((unsigned int*)data[k][i][j])[l] = (i * width + j) % 2049;
223                           if (channelTypes[k] == 1)
224                               ((half*)data[k][i][j])[l] = (i * width + j) % 2049;
225                           if (channelTypes[k] == 2)
226                               ((float*)data[k][i][j])[l] = (i * width + j) % 2049;
227                       }
228                    }
229                 }
230 
231         }
232     }
233 
234     cout << " data prepared, writing ...";
235 
236     file.writePixels(height);
237     cout << " data written\n";
238 
239 }
240 
241 void
readFile(int channelCount,bool bulkRead,const std::string & fn)242 readFile (int channelCount, bool bulkRead, const std::string & fn)
243 {
244     cout << "reading \n" << flush;
245 
246     DeepScanLineInputFile file (fn.c_str(), 8);
247 
248     const Header& fileHeader = file.header();
249     assert (fileHeader.displayWindow() == header.displayWindow());
250     assert (fileHeader.dataWindow() == header.dataWindow());
251     assert (fileHeader.pixelAspectRatio() == header.pixelAspectRatio());
252     assert (fileHeader.screenWindowCenter() == header.screenWindowCenter());
253     assert (fileHeader.screenWindowWidth() == header.screenWindowWidth());
254     assert (fileHeader.lineOrder() == header.lineOrder());
255     assert (fileHeader.compression() == header.compression());
256     assert (fileHeader.channels() == header.channels());
257     assert (fileHeader.type() == header.type());
258 
259     Array2D<unsigned int> localSampleCount;
260     localSampleCount.resizeErase(height, width);
261     Array<Array2D< void* > > data(channelCount);
262     for (int i = 0; i < channelCount; i++)
263         data[i].resizeErase(height, width);
264 
265     DeepFrameBuffer frameBuffer;
266 
267     frameBuffer.insertSampleCountSlice (Slice (IMF::UINT,                    // type // 7
268                                         (char *) (&localSampleCount[0][0]
269                                                   - dataWindow.min.x
270                                                   - dataWindow.min.y * width),               // base // 8)
271                                         sizeof (unsigned int) * 1,          // xStride// 9
272                                         sizeof (unsigned int) * width));    // yStride// 10
273 
274     uint64_t bytes_per_sample=0;
275 
276     for (int i = 0; i < channelCount; i++)
277     {
278         PixelType type = NUM_PIXELTYPES;
279         if (channelTypes[i] == 0)
280             type = IMF::UINT;
281         if (channelTypes[i] == 1)
282             type = IMF::HALF;
283         if (channelTypes[i] == 2)
284             type = IMF::FLOAT;
285 
286         stringstream ss;
287         ss << i;
288         string str = ss.str();
289 
290         int sampleSize = 0;
291         if (channelTypes[i] == 0) sampleSize = sizeof (unsigned int);
292         if (channelTypes[i] == 1) sampleSize = sizeof (half);
293         if (channelTypes[i] == 2) sampleSize = sizeof (float);
294 
295         int pointerSize = sizeof (char *);
296 
297         bytes_per_sample+=sampleSize;
298 
299         frameBuffer.insert (str,                            // name // 6
300                             DeepSlice (type,                    // type // 7
301                             (char *) (&data[i][0][0]
302                                       - dataWindow.min.x
303                                       - dataWindow.min.y * width),               // base // 8)
304                             pointerSize * 1,          // xStride// 9
305                             pointerSize * width,      // yStride// 10
306                             sampleSize));             // sampleStride
307     }
308 
309     file.setFrameBuffer(frameBuffer);
310 
311     file.readPixelSampleCounts(dataWindow.min.y, dataWindow.max.y);
312     uint64_t total_pixel_count = 0;
313 
314     for (int i = 0; i < dataWindow.max.y - dataWindow.min.y + 1; i++)
315     {
316          for (int j = 0; j < width; j++)
317          {
318               assert(localSampleCount[i][j] == sampleCount[i][j]);
319               total_pixel_count += localSampleCount[i][j];
320          }
321     }
322 
323 
324     vector<char> localstorage(total_pixel_count*bytes_per_sample);
325 
326     uint64_t write_pointer=0;
327 
328     for (int i = 0; i < height; i++)
329     {
330         //
331         // Fill in data at the last minute.
332         //
333 
334 
335         for (int j = 0; j < width; j++)
336         {
337             for (int k = 0; k < channelCount; k++)
338             {
339                 data[k][i][j]=&localstorage[write_pointer];
340                 if (channelTypes[k] == 0)
341                     write_pointer+=sizeof(int)*sampleCount[i][j];
342                 if (channelTypes[k] == 1)
343                     write_pointer+=sizeof(half)*sampleCount[i][j];
344                 if (channelTypes[k] == 2)
345                     write_pointer+=sizeof(float)*sampleCount[i][j];
346             }
347         }
348     }
349 
350     cout << "reading image data ... " << flush;
351 
352     file.readPixels(dataWindow.min.y, dataWindow.max.y);
353 
354     cout << " image read \n" << flush;
355 
356 }
357 
358 void
readWriteTest(int channelCount,int testTimes,bool random_channel_data,const std::string & fn)359 readWriteTest (int channelCount,
360                int testTimes,
361                bool random_channel_data,
362                const std::string & fn)
363 {
364     cout << "Testing files with " << channelCount << " channels " << testTimes << " times."
365          << endl << flush;
366     for (int i = 0; i < testTimes; i++)
367     {
368         int compressionIndex = i % 3;
369         Compression compression;
370         switch (compressionIndex)
371         {
372             case 0:
373                 compression = NO_COMPRESSION;
374                 break;
375             case 1:
376                 compression = RLE_COMPRESSION;
377                 break;
378             case 2:
379                 compression = ZIPS_COMPRESSION;
380                 break;
381         }
382 
383         generateRandomFile (channelCount, compression, random_channel_data, fn);
384         readFile (channelCount, false, fn);
385         remove (fn.c_str());
386 
387     }
388 }
389 
390 }; // namespace
391 
testDeepScanLineHuge(const std::string & tempDir)392 void testDeepScanLineHuge (const std::string & tempDir)
393 {
394     try
395     {
396         cout << "\n\nTesting the DeepScanLineInput/OutputFile for huge scanlines:\n" << endl;
397 
398         random_reseed(1);
399         std::string fn = tempDir + "imf_test_deep_scanline_huge.exr";
400 
401         readWriteTest (10, 5 , false, fn);
402         readWriteTest (10, 5 , true,  fn);
403         cout << "ok\n" << endl;
404     }
405     catch (const std::exception &e)
406     {
407         cerr << "ERROR -- caught exception: " << e.what() << endl;
408         assert (false);
409     }
410 }
411