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