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 <ImfRgbaFile.h>
11 #include <ImfTiledRgbaFile.h>
12 #include <ImfMultiPartInputFile.h>
13 #include <ImfMultiPartOutputFile.h>
14 #include <ImfPartType.h>
15 #include <ImfInputPart.h>
16 #include <ImfOutputPart.h>
17 #include <ImfStdIO.h>
18 #include <ImfArray.h>
19 
20 #include <stdio.h>
21 #include <assert.h>
22 #include "Iex.h"
23 #include <errno.h>
24 
25 #include <vector>
26 #include <ImfChannelList.h>
27 
28 #include "TestUtilFStream.h"
29 
30 using namespace OPENEXR_IMF_NAMESPACE;
31 using namespace std;
32 using namespace IMATH_NAMESPACE;
33 
34 namespace {
35 
36 void
fillPixels1(Array2D<Rgba> & pixels,int w,int h)37 fillPixels1 (Array2D<Rgba> &pixels, int w, int h)
38 {
39     for (int y = 0; y < h; ++y)
40     {
41 	for (int x = 0; x < w; ++x)
42 	{
43 	    Rgba &p = pixels[y][x];
44 
45 	    p.r = (x & 1);
46 	    p.g = ((x + y) & 1);
47 	    p.b = (y & 1);
48 	    p.a = (p.r + p.b + p.g) / 3.0;
49 	}
50     }
51 }
52 
53 
54 void
fillPixels2(Array2D<Rgba> & pixels,int w,int h)55 fillPixels2 (Array2D<Rgba> &pixels, int w, int h)
56 {
57     for (int y = 0; y < h; ++y)
58     {
59 	for (int x = 0; x < w; ++x)
60 	{
61 	    Rgba &p = pixels[y][x];
62 
63 	    p.r = (x & 2);
64 	    p.g = ((x + y) & 2);
65 	    p.b = (y & 2);
66 	    p.a = (p.r + p.b + p.g) / 3.0;
67 	}
68     }
69 }
70 
71 
72 //
73 // class MMIFStream -- a memory-mapped implementation of
74 // class IStream based on class std::ifstream
75 //
76 
77 class MMIFStream: public OPENEXR_IMF_NAMESPACE::IStream
78 {
79   public:
80 
81     //-------------------------------------------------------
82     // A constructor that opens the file with the given name.
83     // It reads the whole file into an internal buffer and
84     // then immediately closes the file.
85     //-------------------------------------------------------
86 
87     MMIFStream (const char fileName[]);
88 
89     virtual ~MMIFStream ();
90 
isMemoryMapped() const91     virtual bool        isMemoryMapped () const {return true;}
92 
93     virtual bool	read (char c[/*n*/], int n);
94     virtual char*       readMemoryMapped (int n);
tellg()95     virtual uint64_t	tellg () {return _pos;}
seekg(uint64_t pos)96     virtual void	seekg (uint64_t pos) {_pos = pos;}
clear()97     virtual void	clear () {}
98 
99   private:
100 
101     char*               _buffer;
102     uint64_t            _length;
103     uint64_t            _pos;
104 };
105 
106 
107 
MMIFStream(const char fileName[])108 MMIFStream::MMIFStream (const char fileName[]):
109     OPENEXR_IMF_NAMESPACE::IStream (fileName),
110     _buffer (0),
111     _length (0),
112     _pos (0)
113 {
114     std::ifstream ifs;
115     testutil::OpenStreamWithUTF8Name (
116         ifs, fileName, ios::in | ios_base::binary);
117 
118     //
119     // Get length of file
120     //
121 
122     ifs.seekg (0, ios::end);
123     _length = ifs.tellg();
124     ifs.seekg (0, ios::beg);
125 
126     //
127     // Allocate memory
128     //
129 
130     _buffer = new char [_length];
131 
132     //
133     // Read the entire file
134     //
135 
136     ifs.read (_buffer, _length);
137     ifs.close();
138 }
139 
140 
~MMIFStream()141 MMIFStream::~MMIFStream ()
142 {
143     delete [] _buffer;
144 }
145 
146 
147 bool
read(char c[],int n)148 MMIFStream::read (char c[/*n*/], int n)
149 {
150     if (_pos >= _length && n != 0)
151 	throw IEX_NAMESPACE::InputExc ("Unexpected end of file.");
152 
153     uint64_t n2 = n;
154     bool retVal = true;
155 
156     if (_length - _pos <= n2)
157     {
158         n2 = _length - _pos;
159         retVal = false;
160     }
161 
162     memcpy (c, &(_buffer[_pos]), n2);
163     _pos += n2;
164     return retVal;
165 }
166 
167 
168 char*
readMemoryMapped(int n)169 MMIFStream::readMemoryMapped (int n)
170 {
171     if (_pos >= _length)
172 	throw IEX_NAMESPACE::InputExc ("Unexpected end of file.");
173 
174     if (_pos + n > _length)
175         throw IEX_NAMESPACE::InputExc ("Reading past end of file.");
176 
177     char* retVal = &(_buffer[_pos]);
178     _pos += n;
179     return retVal;
180 }
181 
182 
183 void
writeReadScanLines(const char fileName[],int width,int height,const Array2D<Rgba> & p1)184 writeReadScanLines (const char fileName[],
185 		    int width,
186 		    int height,
187 		    const Array2D<Rgba> &p1)
188 {
189     //
190     // Save a scanline-based RGBA image, but instead of
191     // letting the RgbaOutputFile object open the file,
192     // make the RgbaOutputFile object use an existing
193     // StdOFStream.  Read the image back, using an
194     // existing StdIFStream, and compare the pixels
195     // with the original data.  Then read the image
196     // back a second time using a memory-mapped
197     // MMIFStream (see above).
198     //
199 
200     cout << "scan-line based file:" << endl;
201 
202     {
203         cout << "writing";
204         remove (fileName);
205         std::ofstream os;
206         testutil::OpenStreamWithUTF8Name (
207             os, fileName, ios::out | ios_base::binary);
208         StdOFStream ofs (os, fileName);
209         Header header (width, height);
210         RgbaOutputFile out (ofs, header, WRITE_RGBA);
211         out.setFrameBuffer (&p1[0][0], 1, width);
212         out.writePixels (height);
213     }
214 
215     {
216         cout << ", reading";
217         std::ifstream is;
218         testutil::OpenStreamWithUTF8Name (
219             is, fileName, ios::in | ios_base::binary);
220         StdIFStream ifs (is, fileName);
221         RgbaInputFile in (ifs);
222 
223 	const Box2i &dw = in.dataWindow();
224 	int w = dw.max.x - dw.min.x + 1;
225 	int h = dw.max.y - dw.min.y + 1;
226 	int dx = dw.min.x;
227 	int dy = dw.min.y;
228 
229 	Array2D<Rgba> p2 (h, w);
230 	in.setFrameBuffer (&p2[-dy][-dx], 1, w);
231 	in.readPixels (dw.min.y, dw.max.y);
232 
233         cout << ", comparing";
234 	for (int y = 0; y < h; ++y)
235 	{
236 	    for (int x = 0; x < w; ++x)
237 	    {
238 		assert (p2[y][x].r == p1[y][x].r);
239 		assert (p2[y][x].g == p1[y][x].g);
240 		assert (p2[y][x].b == p1[y][x].b);
241 		assert (p2[y][x].a == p1[y][x].a);
242 	    }
243 	}
244     }
245 
246     {
247         cout << ", reading (memory-mapped)";
248 	MMIFStream ifs (fileName);
249 	RgbaInputFile in (ifs);
250 
251 	const Box2i &dw = in.dataWindow();
252 	int w = dw.max.x - dw.min.x + 1;
253 	int h = dw.max.y - dw.min.y + 1;
254 	int dx = dw.min.x;
255 	int dy = dw.min.y;
256 
257 	Array2D<Rgba> p2 (h, w);
258 	in.setFrameBuffer (&p2[-dy][-dx], 1, w);
259 	in.readPixels (dw.min.y, dw.max.y);
260 
261         cout << ", comparing";
262 	for (int y = 0; y < h; ++y)
263 	{
264 	    for (int x = 0; x < w; ++x)
265 	    {
266 		assert (p2[y][x].r == p1[y][x].r);
267 		assert (p2[y][x].g == p1[y][x].g);
268 		assert (p2[y][x].b == p1[y][x].b);
269 		assert (p2[y][x].a == p1[y][x].a);
270 	    }
271 	}
272     }
273 
274     cout << endl;
275 
276     remove (fileName);
277 }
278 void
writeReadMultiPart(const char fileName[],int width,int height,const Array2D<Rgba> & p1)279 writeReadMultiPart (const char fileName[],
280                     int width,
281                     int height,
282                     const Array2D<Rgba> &p1)
283 {
284     //
285     // Save a two scanline parts in an image, but instead of
286     // letting the MultiPartOutputFile object open the file,
287     // make the MultiPartOutputFile object use an existing
288     // StdOFStream.  Read the image back, using an
289     // existing StdIFStream, and compare the pixels
290     // with the original data.  Then read the image
291     // back a second time using a memory-mapped
292     // MMIFStream (see above).
293     //
294 
295     cout << "scan-line based mulitpart file:" << endl;
296 
297     {
298         cout << "writing";
299         remove (fileName);
300         std::ofstream os;
301         testutil::OpenStreamWithUTF8Name (
302             os, fileName, ios::out | ios_base::binary);
303         StdOFStream ofs (os, fileName);
304 
305         vector<Header> headers(2);
306         headers[0] = Header(width, height);
307         headers[0].setName("part1");
308         headers[0].channels().insert("R",Channel());
309         headers[0].channels().insert("G",Channel());
310         headers[0].channels().insert("B",Channel());
311         headers[0].channels().insert("A",Channel());
312         headers[0].setType(SCANLINEIMAGE);
313 
314         headers[1]=headers[0];
315         headers[1].setName("part2");
316 
317         MultiPartOutputFile out (ofs, &headers[0],2);
318         FrameBuffer f;
319         f.insert("R",Slice(HALF,(char *) &p1[0][0].r,sizeof(Rgba),width*sizeof(Rgba)));
320         f.insert("G",Slice(HALF,(char *) &p1[0][0].g,sizeof(Rgba),width*sizeof(Rgba)));
321         f.insert("B",Slice(HALF,(char *) &p1[0][0].b,sizeof(Rgba),width*sizeof(Rgba)));
322         f.insert("A",Slice(HALF,(char *) &p1[0][0].a,sizeof(Rgba),width*sizeof(Rgba)));
323 
324         for(int i=0;i<2;i++)
325         {
326             OutputPart p(out,i);
327             p.setFrameBuffer (f);
328             p.writePixels (height);
329         }
330     }
331 
332     {
333         cout << ", reading";
334         std::ifstream is;
335         testutil::OpenStreamWithUTF8Name (
336             is, fileName, ios::in | ios_base::binary);
337         StdIFStream ifs (is, fileName);
338         MultiPartInputFile in (ifs);
339 
340         assert(in.parts() == 2);
341 
342         assert(in.header(0).dataWindow()==in.header(1).dataWindow());
343 
344         const Box2i &dw = in.header(0).dataWindow();
345         int w = dw.max.x - dw.min.x + 1;
346         int h = dw.max.y - dw.min.y + 1;
347         int dx = dw.min.x;
348         int dy = dw.min.y;
349 
350         Array2D<Rgba> p2 (h, w);
351         FrameBuffer f;
352         f.insert("R",Slice(HALF,(char *) &p2[-dy][-dx].r,sizeof(Rgba),w*sizeof(Rgba)));
353         f.insert("G",Slice(HALF,(char *) &p2[-dy][-dx].g,sizeof(Rgba),w*sizeof(Rgba)));
354         f.insert("B",Slice(HALF,(char *) &p2[-dy][-dx].b,sizeof(Rgba),w*sizeof(Rgba)));
355         f.insert("A",Slice(HALF,(char *) &p2[-dy][-dx].a,sizeof(Rgba),w*sizeof(Rgba)));
356 
357         for(int part=0;part<2;part++)
358         {
359             InputPart p(in,part);
360             p.setFrameBuffer(f);
361             p.readPixels (dw.min.y, dw.max.y);
362 
363             cout << ", comparing pt " << part;
364             for (int y = 0; y < h; ++y)
365             {
366                 for (int x = 0; x < w; ++x)
367                 {
368                     assert (p2[y][x].r == p1[y][x].r);
369                     assert (p2[y][x].g == p1[y][x].g);
370                     assert (p2[y][x].b == p1[y][x].b);
371                     assert (p2[y][x].a == p1[y][x].a);
372                 }
373             }
374         }
375     }
376 
377     {
378         cout << ", reading (memory-mapped)";
379         MMIFStream ifs (fileName);
380         MultiPartInputFile in (ifs);
381 
382         assert(in.parts() == 2);
383 
384         assert(in.header(0).dataWindow()==in.header(1).dataWindow());
385 
386 
387         const Box2i &dw = in.header(0).dataWindow();
388         int w = dw.max.x - dw.min.x + 1;
389         int h = dw.max.y - dw.min.y + 1;
390         int dx = dw.min.x;
391         int dy = dw.min.y;
392 
393         Array2D<Rgba> p2 (h, w);
394         FrameBuffer f;
395         f.insert("R",Slice(HALF,(char *) &p2[-dy][-dx].r,sizeof(Rgba),w*sizeof(Rgba)));
396         f.insert("G",Slice(HALF,(char *) &p2[-dy][-dx].g,sizeof(Rgba),w*sizeof(Rgba)));
397         f.insert("B",Slice(HALF,(char *) &p2[-dy][-dx].b,sizeof(Rgba),w*sizeof(Rgba)));
398         f.insert("A",Slice(HALF,(char *) &p2[-dy][-dx].a,sizeof(Rgba),w*sizeof(Rgba)));
399 
400         for(int part=0;part<2;part++)
401         {
402             InputPart p(in,part);
403             p.setFrameBuffer(f);
404             p.readPixels (dw.min.y, dw.max.y);
405 
406             cout << ", comparing pt " << part;
407             for (int y = 0; y < h; ++y)
408             {
409                 for (int x = 0; x < w; ++x)
410                 {
411                     assert (p2[y][x].r == p1[y][x].r);
412                     assert (p2[y][x].g == p1[y][x].g);
413                     assert (p2[y][x].b == p1[y][x].b);
414                     assert (p2[y][x].a == p1[y][x].a);
415                 }
416             }
417         }
418     }
419 
420     cout << endl;
421 
422     remove (fileName);
423 }
424 
425 
426 
427 void
writeReadTiles(const char fileName[],int width,int height,const Array2D<Rgba> & p1)428 writeReadTiles (const char fileName[],
429 		int width,
430 		int height,
431 		const Array2D<Rgba> &p1)
432 {
433     //
434     // Save a tiled RGBA image, but instead of letting
435     // the TiledRgbaOutputFile object open the file, make
436     // it use an existing StdOFStream.  Read the image back,
437     // using an existing StdIFStream, and compare the pixels
438     // with the original data.  Then read the image back a
439     // second time using a memory-mapped MMIFStream (see above).
440     //
441 
442     cout << "tiled file:" << endl;
443 
444     {
445         cout << "writing";
446         remove (fileName);
447         std::ofstream os;
448         testutil::OpenStreamWithUTF8Name (
449             os, fileName, ios_base::out | ios_base::binary);
450         StdOFStream ofs (os, fileName);
451         Header header (width, height);
452         TiledRgbaOutputFile out (ofs, header, WRITE_RGBA, 20, 20, ONE_LEVEL);
453         out.setFrameBuffer (&p1[0][0], 1, width);
454         out.writeTiles (0, out.numXTiles() - 1, 0, out.numYTiles() - 1);
455     }
456 
457     {
458         cout << ", reading";
459         std::ifstream is;
460         testutil::OpenStreamWithUTF8Name (
461             is, fileName, ios::in | ios_base::binary);
462         StdIFStream ifs (is, fileName);
463         TiledRgbaInputFile in (ifs);
464 
465 	const Box2i &dw = in.dataWindow();
466 	int w = dw.max.x - dw.min.x + 1;
467 	int h = dw.max.y - dw.min.y + 1;
468 	int dx = dw.min.x;
469 	int dy = dw.min.y;
470 
471 	Array2D<Rgba> p2 (h, w);
472 	in.setFrameBuffer (&p2[-dy][-dx], 1, w);
473         in.readTiles (0, in.numXTiles() - 1, 0, in.numYTiles() - 1);
474 
475         cout << ", comparing";
476 	for (int y = 0; y < h; ++y)
477 	{
478 	    for (int x = 0; x < w; ++x)
479 	    {
480 		assert (p2[y][x].r == p1[y][x].r);
481 		assert (p2[y][x].g == p1[y][x].g);
482 		assert (p2[y][x].b == p1[y][x].b);
483 		assert (p2[y][x].a == p1[y][x].a);
484 	    }
485 	}
486     }
487 
488     {
489         cout << ", reading (memory-mapped)";
490 	MMIFStream ifs (fileName);
491 	TiledRgbaInputFile in (ifs);
492 
493 	const Box2i &dw = in.dataWindow();
494 	int w = dw.max.x - dw.min.x + 1;
495 	int h = dw.max.y - dw.min.y + 1;
496 	int dx = dw.min.x;
497 	int dy = dw.min.y;
498 
499 	Array2D<Rgba> p2 (h, w);
500 	in.setFrameBuffer (&p2[-dy][-dx], 1, w);
501         in.readTiles (0, in.numXTiles() - 1, 0, in.numYTiles() - 1);
502 
503         cout << ", comparing";
504 	for (int y = 0; y < h; ++y)
505 	{
506 	    for (int x = 0; x < w; ++x)
507 	    {
508 		assert (p2[y][x].r == p1[y][x].r);
509 		assert (p2[y][x].g == p1[y][x].g);
510 		assert (p2[y][x].b == p1[y][x].b);
511 		assert (p2[y][x].a == p1[y][x].a);
512 	    }
513 	}
514     }
515 
516     cout << endl;
517 
518     remove (fileName);
519 }
520 
521 
522 //
523 // stringstream version
524 //
525 void
writeReadScanLines(int width,int height,const Array2D<Rgba> & p1)526 writeReadScanLines (int width,
527 		    int height,
528 		    const Array2D<Rgba> &p1)
529 {
530     //
531     // Save a scanline-based RGBA image, but instead of
532     // letting the RgbaOutputFile object open the file,
533     // make the RgbaOutputFile object use an existing
534     // StdOSStream.  Read the image back, using an
535     // existing StdISStream, and compare the pixels
536     // with the original data.
537     //
538 
539     cout << "scan-line based stringstream:" << endl;
540 
541     std::string strEXRFile;
542 
543     {
544         cout << "writing";
545         StdOSStream oss;
546         Header header (width, height);
547         RgbaOutputFile out (oss, header, WRITE_RGBA);
548         out.setFrameBuffer (&p1[0][0], 1, width);
549         out.writePixels (height);
550         strEXRFile = oss.str();
551     }
552 
553     {
554         cout << ", reading";
555         StdISStream iss;
556         iss.clear();
557         iss.str(strEXRFile);
558         RgbaInputFile in (iss);
559 
560 	const Box2i &dw = in.dataWindow();
561 	int w = dw.max.x - dw.min.x + 1;
562 	int h = dw.max.y - dw.min.y + 1;
563 	int dx = dw.min.x;
564 	int dy = dw.min.y;
565 
566 	Array2D<Rgba> p2 (h, w);
567 	in.setFrameBuffer (&p2[-dy][-dx], 1, w);
568 	in.readPixels (dw.min.y, dw.max.y);
569 
570         cout << ", comparing";
571 	for (int y = 0; y < h; ++y)
572 	{
573 	    for (int x = 0; x < w; ++x)
574 	    {
575 		assert (p2[y][x].r == p1[y][x].r);
576 		assert (p2[y][x].g == p1[y][x].g);
577 		assert (p2[y][x].b == p1[y][x].b);
578 		assert (p2[y][x].a == p1[y][x].a);
579 	    }
580 	}
581     }
582 
583     cout << endl;
584 }
585 
586 
587 //
588 // stringstream version
589 //
590 void
writeReadMultiPart(int width,int height,const Array2D<Rgba> & p1)591 writeReadMultiPart (int width,
592                     int height,
593                     const Array2D<Rgba> &p1)
594 {
595     //
596     // Save a two scanline parts in an image, but instead of
597     // letting the MultiPartOutputFile object open the file,
598     // make the MultiPartOutputFile object use an existing
599     // StdOSStream.  Read the image back, using an
600     // existing StdISStream, and compare the pixels
601     // with the original data.
602     //
603 
604     cout << "scan-line based mulitpart stringstream:" << endl;
605 
606     std::string strEXRFile;
607 
608     {
609         cout << "writing";
610         StdOSStream oss;
611 
612         vector<Header> headers(2);
613         headers[0] = Header(width, height);
614         headers[0].setName("part1");
615         headers[0].channels().insert("R",Channel());
616         headers[0].channels().insert("G",Channel());
617         headers[0].channels().insert("B",Channel());
618         headers[0].channels().insert("A",Channel());
619         headers[0].setType(SCANLINEIMAGE);
620 
621         headers[1]=headers[0];
622         headers[1].setName("part2");
623 
624         MultiPartOutputFile out (oss, &headers[0],2);
625         FrameBuffer f;
626         f.insert("R",Slice(HALF,(char *) &p1[0][0].r,sizeof(Rgba),width*sizeof(Rgba)));
627         f.insert("G",Slice(HALF,(char *) &p1[0][0].g,sizeof(Rgba),width*sizeof(Rgba)));
628         f.insert("B",Slice(HALF,(char *) &p1[0][0].b,sizeof(Rgba),width*sizeof(Rgba)));
629         f.insert("A",Slice(HALF,(char *) &p1[0][0].a,sizeof(Rgba),width*sizeof(Rgba)));
630 
631         for(int i=0;i<2;i++)
632         {
633             OutputPart p(out,i);
634             p.setFrameBuffer (f);
635             p.writePixels (height);
636         }
637 
638         strEXRFile = oss.str();
639     }
640 
641     {
642         cout << ", reading";
643         StdISStream iss;
644         iss.clear();
645         iss.str(strEXRFile);
646         MultiPartInputFile in (iss);
647 
648         assert(in.parts() == 2);
649 
650         assert(in.header(0).dataWindow()==in.header(1).dataWindow());
651 
652         const Box2i &dw = in.header(0).dataWindow();
653         int w = dw.max.x - dw.min.x + 1;
654         int h = dw.max.y - dw.min.y + 1;
655         int dx = dw.min.x;
656         int dy = dw.min.y;
657 
658         Array2D<Rgba> p2 (h, w);
659         FrameBuffer f;
660         f.insert("R",Slice(HALF,(char *) &p2[-dy][-dx].r,sizeof(Rgba),w*sizeof(Rgba)));
661         f.insert("G",Slice(HALF,(char *) &p2[-dy][-dx].g,sizeof(Rgba),w*sizeof(Rgba)));
662         f.insert("B",Slice(HALF,(char *) &p2[-dy][-dx].b,sizeof(Rgba),w*sizeof(Rgba)));
663         f.insert("A",Slice(HALF,(char *) &p2[-dy][-dx].a,sizeof(Rgba),w*sizeof(Rgba)));
664 
665         for(int part=0;part<2;part++)
666         {
667             InputPart p(in,part);
668             p.setFrameBuffer(f);
669             p.readPixels (dw.min.y, dw.max.y);
670 
671             cout << ", comparing pt " << part;
672             for (int y = 0; y < h; ++y)
673             {
674                 for (int x = 0; x < w; ++x)
675                 {
676                     assert (p2[y][x].r == p1[y][x].r);
677                     assert (p2[y][x].g == p1[y][x].g);
678                     assert (p2[y][x].b == p1[y][x].b);
679                     assert (p2[y][x].a == p1[y][x].a);
680                 }
681             }
682         }
683     }
684 
685     cout << endl;
686 }
687 
688 //
689 // stringstream version
690 //
691 void
writeReadTiles(int width,int height,const Array2D<Rgba> & p1)692 writeReadTiles (int width,
693 		int height,
694 		const Array2D<Rgba> &p1)
695 {
696     //
697     // Save a tiled RGBA image, but instead of letting
698     // the TiledRgbaOutputFile object open the file, make
699     // it use an existing StdOSStream.  Read the image back,
700     // using an existing StdISStream, and compare the pixels
701     // with the original data.
702     //
703 
704     cout << "tiled stringstream:" << endl;
705 
706     std::string strEXRFile;
707 
708     {
709         cout << "writing";
710         StdOSStream oss;
711         Header header (width, height);
712         TiledRgbaOutputFile out (oss, header, WRITE_RGBA, 20, 20, ONE_LEVEL);
713         out.setFrameBuffer (&p1[0][0], 1, width);
714         out.writeTiles (0, out.numXTiles() - 1, 0, out.numYTiles() - 1);
715 
716         strEXRFile = oss.str();
717     }
718 
719     {
720         cout << ", reading";
721         StdISStream iss;
722         iss.clear();
723         iss.str(strEXRFile);
724         TiledRgbaInputFile in (iss);
725 
726 	const Box2i &dw = in.dataWindow();
727 	int w = dw.max.x - dw.min.x + 1;
728 	int h = dw.max.y - dw.min.y + 1;
729 	int dx = dw.min.x;
730 	int dy = dw.min.y;
731 
732 	Array2D<Rgba> p2 (h, w);
733 	in.setFrameBuffer (&p2[-dy][-dx], 1, w);
734         in.readTiles (0, in.numXTiles() - 1, 0, in.numYTiles() - 1);
735 
736         cout << ", comparing";
737 	for (int y = 0; y < h; ++y)
738 	{
739 	    for (int x = 0; x < w; ++x)
740 	    {
741 		assert (p2[y][x].r == p1[y][x].r);
742 		assert (p2[y][x].g == p1[y][x].g);
743 		assert (p2[y][x].b == p1[y][x].b);
744 		assert (p2[y][x].a == p1[y][x].a);
745 	    }
746 	}
747     }
748 
749     cout << endl;
750 }
751 
752 } // namespace
753 
754 
755 void
testExistingStreams(const std::string & tempDir)756 testExistingStreams (const std::string &tempDir)
757 {
758     try
759     {
760         cout << "Testing reading and writing using existing streams" << endl;
761 
762         const int W = 119;
763         const int H = 237;
764 
765         Array2D<Rgba> p1 (H, W);
766 
767         fillPixels1 (p1, W, H);
768         writeReadScanLines ((tempDir + "imf_test_streams.exr").c_str(), W, H, p1);
769         writeReadScanLines (W, H, p1);
770 
771         fillPixels2 (p1, W, H);
772         writeReadTiles ((tempDir + "imf_test_streams2.exr").c_str(), W, H, p1);
773         writeReadTiles (W, H, p1);
774 
775         fillPixels1 (p1, W, H);
776         writeReadMultiPart ((tempDir +  "imf_test_streams3.exr").c_str(), W, H, p1);
777         writeReadMultiPart (W, H, p1);
778 
779         cout << "ok\n" << endl;
780     }
781     catch (const std::exception &e)
782     {
783 	cerr << "ERROR -- caught exception: " << e.what() << endl;
784 	assert (false);
785     }
786 }
787