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 <ImfTiledOutputFile.h>
11 #include <ImfTiledInputFile.h>
12 #include <ImfInputFile.h>
13 #include <ImfTiledRgbaFile.h>
14 #include <ImfRgbaFile.h>
15 #include <ImfArray.h>
16 #include <ImfChannelList.h>
17 #include <ImfThreading.h>
18 #include "IlmThread.h"
19 #include "ImathRandom.h"
20 #include <string>
21 #include <stdio.h>
22 #include <assert.h>
23 #include <vector>
24 #include <math.h>
25 #include <ImfTileDescriptionAttribute.h>
26 
27 
28 namespace IMF = OPENEXR_IMF_NAMESPACE;
29 using namespace IMF;
30 using namespace std;
31 using namespace IMATH_NAMESPACE;
32 
33 namespace {
34 
35 
36 void
fillPixels(Array2D<unsigned int> & pi,Array2D<half> & ph,Array2D<float> & pf,int width,int height)37 fillPixels (Array2D<unsigned int> &pi,
38             Array2D<half> &ph,
39             Array2D<float> &pf,
40             int width,
41             int height)
42 {
43     for (int y = 0; y < height; ++y)
44         for (int x = 0; x < width; ++x)
45         {
46             pi[y][x] = x % 100 + 100 * (y % 100);
47             ph[y][x] = sin (double (x)) + sin (y * 0.5);
48             pf[y][x] = sin (double (y)) + sin (x * 0.5);
49         }
50 }
51 
52 
53 void
writeRead(const Array2D<unsigned int> & pi1,const Array2D<half> & ph1,const Array2D<float> & pf1,const char fileName[],LineOrder lorder,int width,int height,int xSize,int ySize,int xOffset,int yOffset,Compression comp,LevelMode mode,LevelRoundingMode rmode,bool fillChannel)54 writeRead (const Array2D<unsigned int> &pi1,
55            const Array2D<half> &ph1,
56            const Array2D<float> &pf1,
57            const char fileName[],
58            LineOrder lorder,
59            int width,
60            int height,
61            int xSize,
62            int ySize,
63            int xOffset,
64            int yOffset,
65            Compression comp,
66            LevelMode mode,
67 	   LevelRoundingMode rmode,
68            bool fillChannel
69           )
70 {
71     //
72     // Write the pixel data in pi1, ph1 and ph2 to a tiled
73     // image file using the specified parameters.
74     // Read the pixel data back from the file using the scanline
75     // interface one scanline at a time, and verify that the data did
76     // not change.
77     // For MIPMAP and RIPMAP_LEVELS, the lower levels of the images
78     // are filled in cropped versions of the level(0,0) image,
79     // i.e. no filtering is done.
80     //
81 
82     cout << "levelMode " << mode <<
83 	    ", roundingMode " << rmode <<
84             ", line order " << lorder <<
85             ",\ntileSize " << xSize << "x" << ySize <<
86             ", xOffset " << xOffset <<
87             ", yOffset "<< yOffset << endl;
88 
89     Header hdr ((Box2i (V2i (0, 0),                     // display window
90                         V2i (width - 1, height -1))),
91                 (Box2i (V2i (xOffset, yOffset),         // data window
92                         V2i (xOffset + width - 1, yOffset + height - 1))));
93     hdr.lineOrder() = lorder;
94     hdr.compression() = comp;
95 
96     hdr.channels().insert ("I", Channel (IMF::UINT));
97     hdr.channels().insert ("H", Channel (IMF::HALF));
98     hdr.channels().insert ("F", Channel (IMF::FLOAT));
99 
100     hdr.setTileDescription(TileDescription(xSize, ySize, mode, rmode));
101     {
102         FrameBuffer fb;
103 
104         fb.insert ("I",                                       // name
105                    Slice (IMF::UINT,                          // type
106                           (char *) &pi1[-yOffset][-xOffset],  // base
107                           sizeof (pi1[0][0]),                 // xStride
108                           sizeof (pi1[0][0]) * width)         // yStride
109                   );
110 
111         fb.insert ("H",                                       // name
112                    Slice (IMF::HALF,                          // type
113                           (char *) &ph1[-yOffset][-xOffset],  // base
114                           sizeof (ph1[0][0]),                 // xStride
115                           sizeof (ph1[0][0]) * width)         // yStride
116                   );
117 
118         fb.insert ("F",                                       // name
119                    Slice (IMF::FLOAT,                              // type
120                           (char *) &pf1[-yOffset][-xOffset],  // base
121                           sizeof (pf1[0][0]),                 // xStride
122                           sizeof (pf1[0][0]) * width)         // yStride
123                   );
124 
125         cout << " writing" << flush;
126 
127         remove (fileName);
128         TiledOutputFile out (fileName, hdr);
129         out.setFrameBuffer (fb);
130 
131         int startTileY = -1;
132 	int endTileY = -1;
133         int dy;
134 
135         switch (mode)
136         {
137           case ONE_LEVEL:
138           {
139             if (lorder == DECREASING_Y)
140             {
141                 startTileY = out.numYTiles() - 1;
142                 endTileY = -1;
143 
144                 dy = -1;
145             }
146             else
147             {
148                 startTileY = 0;
149                 endTileY = out.numYTiles();
150 
151                 dy = 1;
152             }
153 
154             for (int tileY = startTileY; tileY != endTileY; tileY += dy)
155                 for (int tileX = 0; tileX < out.numXTiles(); ++tileX)
156                     out.writeTile (tileX, tileY);
157           }
158           break;
159 
160           case MIPMAP_LEVELS:
161           {
162             if (lorder == DECREASING_Y)
163             {
164                 endTileY = -1;
165                 dy = -1;
166             }
167             else
168             {
169                 startTileY = 0;
170                 dy = 1;
171             }
172 
173             for (int level = 0; level < out.numLevels(); ++level)
174             {
175                 if (lorder == DECREASING_Y)
176                     startTileY = out.numYTiles(level) - 1;
177                 else
178                     endTileY = out.numYTiles(level);
179 
180                 for (int tileY = startTileY; tileY != endTileY; tileY += dy)
181                     for (int tileX = 0; tileX < out.numXTiles(level); ++tileX)
182                         out.writeTile (tileX, tileY, level);
183             }
184           }
185           break;
186 
187           case RIPMAP_LEVELS:
188           {
189             for (int ylevel = 0; ylevel < out.numYLevels(); ++ylevel)
190             {
191                 if (lorder == DECREASING_Y)
192                 {
193                     startTileY = out.numYTiles(ylevel) - 1;
194                     endTileY = -1;
195 
196                     dy = -1;
197                 }
198                 else
199                 {
200                     startTileY = 0;
201                     endTileY = out.numYTiles(ylevel);
202 
203                     dy = 1;
204                 }
205 
206                 for (int xlevel = 0; xlevel < out.numXLevels(); ++xlevel)
207                 {
208                     for (int tileY = startTileY; tileY != endTileY;
209                          tileY += dy)
210                         for (int tileX = 0; tileX < out.numXTiles (xlevel);
211                              ++tileX)
212                             out.writeTile (tileX, tileY, xlevel, ylevel);
213                 }
214             }
215           }
216           break;
217 			case NUM_LEVELMODES:
218 			default:
219 				std::cerr << "Invalid tile mode " << int(mode) << std::endl;
220 				break;
221         }
222     }
223 
224     {
225         cout << " reading INCREASING_Y" << flush;
226 
227         InputFile in (fileName);
228 
229         const Box2i &dw = in.header().dataWindow();
230         int w = dw.max.x - dw.min.x + 1;
231         int h = dw.max.y - dw.min.y + 1;
232         int dwx = dw.min.x;
233         int dwy = dw.min.y;
234 
235         Array2D<unsigned int> pi2 (h, w);
236         Array2D<half>         ph2 (h, w);
237         Array2D<float>        pf2 (h, w);
238 
239         Array2D<unsigned int> fi2 (fillChannel ? h : 1 , fillChannel ? w : 1);
240         Array2D<half>         fh2 (fillChannel ? h : 1 , fillChannel ? w : 1);
241         Array2D<float>        ff2 (fillChannel ? h : 1 , fillChannel ? w : 1);
242 
243 
244         const unsigned int fillInt = 12;
245         const half fillHalf = 4.5;
246         const float fillFloat = M_PI;
247 
248 
249         FrameBuffer fb;
250 
251         fb.insert ("I",                             // name
252                    Slice (IMF::UINT,                // type
253                           (char *) &pi2[-dwy][-dwx],// base
254                           sizeof (pi2[0][0]),       // xStride
255                           sizeof (pi2[0][0]) * w)   // yStride
256                   );
257 
258         fb.insert ("H",                             // name
259                    Slice (IMF::HALF,                // type
260                           (char *) &ph2[-dwy][-dwx],// base
261                           sizeof (ph2[0][0]),       // xStride
262                           sizeof (ph2[0][0]) * w)   // yStride
263                   );
264 
265         fb.insert ("F",                             // name
266                    Slice (IMF::FLOAT,               // type
267                           (char *) &pf2[-dwy][-dwx],// base
268                           sizeof (pf2[0][0]),       // xStride
269                           sizeof (pf2[0][0]) * w)   // yStride
270                   );
271 
272         if(fillChannel)
273         {
274             fb.insert ("FI",                             // name
275                    Slice (IMF::UINT,                // type
276                           (char *) &fi2[-dwy][-dwx],// base
277                           sizeof (fi2[0][0]),       // xStride
278                           sizeof (fi2[0][0]) * w,1,1,fillInt)  // yStride
279                   );
280 
281             fb.insert ("FH",                             // name
282                     Slice (IMF::HALF,                // type
283                             (char *) &fh2[-dwy][-dwx],// base
284                             sizeof (fh2[0][0]),       // xStride
285                             sizeof (fh2[0][0]) * w,1,1,fillHalf)   // yStride
286                     );
287 
288             fb.insert ("FF",                             // name
289                     Slice (IMF::FLOAT,               // type
290                             (char *) &ff2[-dwy][-dwx],// base
291                             sizeof (ff2[0][0]),       // xStride
292                             sizeof (ff2[0][0]) * w,1,1,fillFloat)   // yStride
293                     );
294         }
295 
296         in.setFrameBuffer (fb);
297         for (int y = dw.min.y; y <= dw.max.y; ++y)
298             in.readPixels (y);
299 
300         cout << " comparing" << flush;
301 
302         assert (in.header().displayWindow() == hdr.displayWindow());
303         assert (in.header().dataWindow() == hdr.dataWindow());
304         assert (in.header().pixelAspectRatio() == hdr.pixelAspectRatio());
305         assert (in.header().screenWindowCenter() == hdr.screenWindowCenter());
306         assert (in.header().screenWindowWidth() == hdr.screenWindowWidth());
307         assert (in.header().lineOrder() == hdr.lineOrder());
308         assert (in.header().compression() == hdr.compression());
309 
310         ChannelList::ConstIterator hi = hdr.channels().begin();
311         ChannelList::ConstIterator ii = in.header().channels().begin();
312 
313         while (hi != hdr.channels().end())
314         {
315             assert (!strcmp (hi.name(), ii.name()));
316             assert (hi.channel().type == ii.channel().type);
317             assert (hi.channel().xSampling == ii.channel().xSampling);
318             assert (hi.channel().ySampling == ii.channel().ySampling);
319 
320             ++hi;
321             ++ii;
322         }
323 
324         assert (ii == in.header().channels().end());
325 
326         for (int y = 0; y < h; ++y)
327         {
328             for (int x = 0; x < w; ++x)
329             {
330                 assert (pi1[y][x] == pi2[y][x]);
331                 assert (ph1[y][x] == ph2[y][x]);
332                 assert (pf1[y][x] == pf2[y][x]);
333 
334                 if (fillChannel)
335                 {
336                     assert(fi2[y][x] == fillInt);
337                     assert(fh2[y][x] == fillHalf);
338                     assert(ff2[y][x] == fillFloat);
339                 }
340             }
341         }
342     }
343 
344     {
345         cout << endl << "         reading DECREASING_Y" << flush;
346 
347         InputFile in (fileName);
348 
349         const Box2i &dw = in.header().dataWindow();
350         int w = dw.max.x - dw.min.x + 1;
351         int h = dw.max.y - dw.min.y + 1;
352         int dwx = dw.min.x;
353         int dwy = dw.min.y;
354 
355         Array2D<unsigned int> pi2 (h, w);
356         Array2D<half>         ph2 (h, w);
357         Array2D<float>        pf2 (h, w);
358 
359         Array2D<unsigned int> fi2 (fillChannel ? h : 1 , fillChannel ? w : 1);
360         Array2D<half>         fh2 (fillChannel ? h : 1 , fillChannel ? w : 1);
361         Array2D<float>        ff2 (fillChannel ? h : 1 , fillChannel ? w : 1);
362 
363         FrameBuffer fb;
364 
365         fb.insert ("I",                             // name
366                    Slice (IMF::UINT,                // type
367                           (char *) &pi2[-dwy][-dwx],// base
368                           sizeof (pi2[0][0]),       // xStride
369                           sizeof (pi2[0][0]) * w)   // yStride
370                   );
371 
372         fb.insert ("H",                             // name
373                    Slice (IMF::HALF,                // type
374                           (char *) &ph2[-dwy][-dwx],// base
375                           sizeof (ph2[0][0]),       // xStride
376                           sizeof (ph2[0][0]) * w)   // yStride
377                   );
378 
379         fb.insert ("F",                             // name
380                    Slice (IMF::FLOAT,               // type
381                           (char *) &pf2[-dwy][-dwx],// base
382                           sizeof (pf2[0][0]),       // xStride
383                           sizeof (pf2[0][0]) * w)   // yStride
384                   );
385         const unsigned int fillInt = 21;
386         const half fillHalf = 42;
387         const float fillFloat = 2.8;
388 
389         if (fillChannel)
390         {
391             fb.insert ("FI",                             // name
392                    Slice (IMF::UINT,                // type
393                           (char *) &fi2[-dwy][-dwx],// base
394                           sizeof (fi2[0][0]),       // xStride
395                           sizeof (fi2[0][0]) * w,1,1,fillInt)   // yStride
396                   );
397 
398             fb.insert ("FH",                             // name
399                     Slice (IMF::HALF,                // type
400                             (char *) &fh2[-dwy][-dwx],// base
401                             sizeof (fh2[0][0]),       // xStride
402                             sizeof (fh2[0][0]) * w,1,1,fillHalf)   // yStride
403                     );
404 
405             fb.insert ("FF",                             // name
406                     Slice (IMF::FLOAT,               // type
407                             (char *) &ff2[-dwy][-dwx],// base
408                             sizeof (ff2[0][0]),       // xStride
409                             sizeof (ff2[0][0]) * w,1,1,fillFloat)   // yStride
410                     );
411 
412         }
413 
414         in.setFrameBuffer (fb);
415         for (int y = dw.max.y; y >= dw.min.y; --y)
416             in.readPixels (y);
417 
418         cout << " comparing" << flush;
419 
420         assert (in.header().displayWindow() == hdr.displayWindow());
421         assert (in.header().dataWindow() == hdr.dataWindow());
422         assert (in.header().pixelAspectRatio() == hdr.pixelAspectRatio());
423         assert (in.header().screenWindowCenter() == hdr.screenWindowCenter());
424         assert (in.header().screenWindowWidth() == hdr.screenWindowWidth());
425         assert (in.header().lineOrder() == hdr.lineOrder());
426         assert (in.header().compression() == hdr.compression());
427 
428         ChannelList::ConstIterator hi = hdr.channels().begin();
429         ChannelList::ConstIterator ii = in.header().channels().begin();
430 
431         while (hi != hdr.channels().end())
432         {
433             assert (!strcmp (hi.name(), ii.name()));
434             assert (hi.channel().type == ii.channel().type);
435             assert (hi.channel().xSampling == ii.channel().xSampling);
436             assert (hi.channel().ySampling == ii.channel().ySampling);
437 
438             ++hi;
439             ++ii;
440         }
441 
442         assert (ii == in.header().channels().end());
443 
444         for (int y = 0; y < h; ++y)
445         {
446             for (int x = 0; x < w; ++x)
447             {
448                 assert (pi1[y][x] == pi2[y][x]);
449                 assert (ph1[y][x] == ph2[y][x]);
450                 assert (pf1[y][x] == pf2[y][x]);
451                 if (fillChannel)
452                 {
453                     assert(fi2[y][x] == fillInt);
454                     assert(fh2[y][x] == fillHalf);
455                     assert(ff2[y][x] == fillFloat);
456                 }
457             }
458         }
459     }
460 
461     {
462         cout << endl << "         reading INCREASING_Y "
463 		        "(new frame buffer on every line)" << flush;
464 
465         InputFile in (fileName);
466 
467         const Box2i &dw = in.header().dataWindow();
468         int w = dw.max.x - dw.min.x + 1;
469         int h = dw.max.y - dw.min.y + 1;
470         int dwx = dw.min.x;
471         int dwy = dw.min.y;
472 
473         Array2D<unsigned int> pi2 (h, w);
474         Array2D<half>         ph2 (h, w);
475         Array2D<float>        pf2 (h, w);
476 
477 
478         Array2D<unsigned int> fi2 (fillChannel ? h : 1 , fillChannel ? w : 1);
479         Array2D<half>         fh2 (fillChannel ? h : 1 , fillChannel ? w : 1);
480         Array2D<float>        ff2 (fillChannel ? h : 1 , fillChannel ? w : 1);
481 
482 
483         const unsigned int fillInt = 81;
484         const half fillHalf = 0.5;
485         const float fillFloat = 7.8;
486 
487 
488         for (int y = dw.min.y; y <= dw.max.y; ++y)
489 	{
490 	    FrameBuffer fb;
491 
492 	    fb.insert ("I",					// name
493 		       Slice (IMF::UINT,			// type
494 			      (char *) &pi2[y - dwy][-dwx],	// base
495 			      sizeof (pi2[0][0]),		// xStride
496 			      0)				// yStride
497 		      );
498 
499 	    fb.insert ("H",					// name
500 		       Slice (IMF::HALF,			// type
501 			      (char *) &ph2[y - dwy][-dwx],	// base
502 			      sizeof (ph2[0][0]),		// xStride
503 			      0)				// yStride
504 		      );
505 
506 	    fb.insert ("F",                     	        // name
507 		       Slice (IMF::FLOAT,			// type
508 			      (char *) &pf2[y - dwy][-dwx],	// base
509 			      sizeof (pf2[0][0]),		// xStride
510 			      0)				// yStride
511 		      );
512 
513             if (fillChannel)
514             {
515                 fb.insert ("FI",					// name
516                         Slice (IMF::UINT,			// type
517                                 (char *) &fi2[y - dwy][-dwx],	// base
518                                 sizeof (fi2[0][0]),		// xStride
519                                 0,1,1,fillInt)				// yStride
520                         );
521 
522                 fb.insert ("FH",					// name
523                         Slice (IMF::HALF,			// type
524                                 (char *) &fh2[y - dwy][-dwx],	// base
525                                 sizeof (fh2[0][0]),		// xStride
526                                 0,1,1,fillHalf)				// yStride
527                         );
528 
529                 fb.insert ("FF",                     	        // name
530                         Slice (IMF::FLOAT,			// type
531                                 (char *) &ff2[y - dwy][-dwx],	// base
532                                 sizeof (ff2[0][0]),		// xStride
533                                 0,1,1,fillFloat)				// yStride
534                         );
535 
536             }
537 
538 	    in.setFrameBuffer (fb);
539             in.readPixels (y);
540 	}
541 
542         cout << " comparing" << flush;
543 
544         assert (in.header().displayWindow() == hdr.displayWindow());
545         assert (in.header().dataWindow() == hdr.dataWindow());
546         assert (in.header().pixelAspectRatio() == hdr.pixelAspectRatio());
547         assert (in.header().screenWindowCenter() == hdr.screenWindowCenter());
548         assert (in.header().screenWindowWidth() == hdr.screenWindowWidth());
549         assert (in.header().lineOrder() == hdr.lineOrder());
550         assert (in.header().compression() == hdr.compression());
551 
552         ChannelList::ConstIterator hi = hdr.channels().begin();
553         ChannelList::ConstIterator ii = in.header().channels().begin();
554 
555         while (hi != hdr.channels().end())
556         {
557             assert (!strcmp (hi.name(), ii.name()));
558             assert (hi.channel().type == ii.channel().type);
559             assert (hi.channel().xSampling == ii.channel().xSampling);
560             assert (hi.channel().ySampling == ii.channel().ySampling);
561 
562             ++hi;
563             ++ii;
564         }
565 
566         assert (ii == in.header().channels().end());
567 
568         for (int y = 0; y < h; ++y)
569         {
570             for (int x = 0; x < w; ++x)
571             {
572                 assert (pi1[y][x] == pi2[y][x]);
573                 assert (ph1[y][x] == ph2[y][x]);
574                 assert (pf1[y][x] == pf2[y][x]);
575                 if (fillChannel)
576                 {
577                     assert (fi2[y][x] == fillInt);
578                     assert (fh2[y][x] == fillHalf);
579                     assert (ff2[y][x] == fillFloat);
580                 }
581             }
582 
583         }
584     }
585 
586     remove (fileName);
587     cout << endl;
588 }
589 
590 
591 void
writeRead(const std::string & tempDir,const Array2D<unsigned int> & pi,const Array2D<half> & ph,const Array2D<float> & pf,int W,int H,LineOrder lorder,Compression comp,LevelRoundingMode rmode,int dx,int dy,int xSize,int ySize)592 writeRead (const std::string &tempDir,
593            const Array2D<unsigned int> &pi,
594            const Array2D<half> &ph,
595            const Array2D<float> &pf,
596            int W,
597            int H,
598            LineOrder lorder,
599            Compression comp,
600 	   LevelRoundingMode rmode,
601            int dx, int dy,
602            int xSize, int ySize)
603 {
604     std::string filename = tempDir + "imf_test_scanline_api.exr";
605 
606     writeRead (pi, ph, pf, filename.c_str(), lorder, W, H,
607                xSize, ySize, dx, dy, comp, ONE_LEVEL, rmode , false);
608     writeRead (pi, ph, pf, filename.c_str(), lorder, W, H,
609                xSize, ySize, dx, dy, comp, MIPMAP_LEVELS, rmode , false );
610     writeRead (pi, ph, pf, filename.c_str(), lorder, W, H,
611                xSize, ySize, dx, dy, comp, RIPMAP_LEVELS, rmode , false);
612     writeRead (pi, ph, pf, filename.c_str(), lorder, W, H,
613                xSize, ySize, dx, dy, comp, ONE_LEVEL, rmode , true);
614 }
615 
616 } // namespace
617 
618 
619 void
testScanLineApi(const std::string & tempDir)620 testScanLineApi (const std::string &tempDir)
621 {
622     try
623     {
624         cout << "Testing the scanline API for tiled files" << endl;
625 
626         const int W = 48;
627         const int H = 81;
628         const int DX = -17;
629         const int DY = -29;
630 
631         Array2D<unsigned int> pi (H, W);
632         Array2D<half> ph (H, W);
633         Array2D<float> pf (H, W);
634         fillPixels (pi, ph, pf, W, H);
635 
636 	int maxThreads = ILMTHREAD_NAMESPACE::supportsThreads()? 3: 0;
637 
638 	for (int n = 0; n <= maxThreads; ++n)
639 	{
640 	    if (ILMTHREAD_NAMESPACE::supportsThreads())
641 	    {
642 		setGlobalThreadCount (n);
643 		cout << "\nnumber of threads: " << globalThreadCount() << endl;
644 	    }
645 
646 	    for (int lorder = 0; lorder < NUM_LINEORDERS; ++lorder)
647 	    {
648 		for (int rmode = 0; rmode < NUM_ROUNDINGMODES; ++rmode)
649 		{
650 		    writeRead (tempDir, pi, ph, pf,  W, H,
651 			       LineOrder (lorder),
652 			       ZIP_COMPRESSION,
653 			       LevelRoundingMode (rmode),
654 			       0, 0, 1, 1);
655 
656 		    writeRead (tempDir, pi, ph, pf, W, H,
657 			       LineOrder (lorder),
658 			       ZIP_COMPRESSION,
659 			       LevelRoundingMode (rmode),
660 			       DX, DY, 1, 1);
661 
662 		    writeRead (tempDir, pi, ph, pf, W, H,
663 			       LineOrder (lorder),
664 			       ZIP_COMPRESSION,
665 			       LevelRoundingMode (rmode),
666 			       0, 0, 24, 26);
667 
668 		    writeRead (tempDir, pi, ph, pf, W, H,
669 			       LineOrder (lorder),
670 			       ZIP_COMPRESSION,
671 			       LevelRoundingMode (rmode),
672 			       DX, DY, 24, 26);
673 
674 		    writeRead (tempDir, pi, ph, pf, W, H,
675 			       LineOrder (lorder),
676 			       ZIP_COMPRESSION,
677 			       LevelRoundingMode (rmode),
678 			       0, 0, 48, 81);
679 
680 		    writeRead (tempDir, pi, ph, pf, W, H,
681 			       LineOrder (lorder),
682 			       ZIP_COMPRESSION,
683 			       LevelRoundingMode (rmode),
684 			       DX, DY, 48, 81);
685 
686 		    writeRead (tempDir, pi, ph, pf, W, H,
687 			       LineOrder (lorder),
688 			       ZIP_COMPRESSION,
689 			       LevelRoundingMode (rmode),
690 			       0, 0, 128, 96);
691 
692 		    writeRead (tempDir, pi, ph, pf, W, H,
693 			       LineOrder (lorder),
694 			       ZIP_COMPRESSION,
695 			       LevelRoundingMode (rmode),
696 			       DX, DY, 128, 96);
697 		}
698 	    }
699 	}
700 
701         cout << "ok\n" << endl;
702     }
703     catch (const std::exception &e)
704     {
705         cerr << "ERROR -- caught exception: " << e.what() << endl;
706         assert (false);
707     }
708 }
709