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 "compareB44.h"
11 #include "compareDwa.h"
12 
13 #include <ImfTiledRgbaFile.h>
14 #include <ImfTiledOutputFile.h>
15 #include <ImfChannelList.h>
16 #include <ImfArray.h>
17 #include <ImfFrameBuffer.h>
18 #include <ImfHeader.h>
19 #include <ImfThreading.h>
20 #include <IlmThread.h>
21 #include <ImathRandom.h>
22 #include <string>
23 #include <stdio.h>
24 #include <assert.h>
25 #include <vector>
26 #include <math.h>
27 #include <algorithm>
28 
29 
30 using namespace OPENEXR_IMF_NAMESPACE;
31 using namespace std;
32 using namespace IMATH_NAMESPACE;
33 
34 
35 namespace {
36 
37 void
fillPixels(Array2D<Rgba> & pixels,int w,int h)38 fillPixels (Array2D<Rgba> &pixels, int w, int h)
39 {
40     for (int y = 0; y < h; ++y)
41     {
42 	for (int x = 0; x < w; ++x)
43 	{
44 	    Rgba &p = pixels[y][x];
45 
46 	    p.r = 0.5 + 0.5 * sin (0.1 * x + 0.1 * y);
47 	    p.g = 0.5 + 0.5 * sin (0.1 * x + 0.2 * y);
48 	    p.b = 0.5 + 0.5 * sin (0.1 * x + 0.3 * y);
49 	    p.a = (p.r + p.b + p.g) / 3.0;
50 	}
51     }
52 }
53 
54 
55 void
writeReadRGBAONE(const char fileName[],int width,int height,RgbaChannels channels,Compression comp,int xSize,int ySize)56 writeReadRGBAONE (const char fileName[],
57 	          int width,
58 		  int height,
59 		  RgbaChannels channels,
60 		  Compression comp,
61 		  int xSize, int ySize)
62 {
63     cout << "levelMode 0" <<
64             ", compression " << comp <<
65             ", tileSize " << xSize << "x" << ySize << endl;
66 
67     Header header (width, height);
68     header.lineOrder() = INCREASING_Y;
69     header.compression() = comp;
70 
71     Array2D<Rgba> p1 (height, width);
72 
73     {
74         cout << " writing" << flush;
75 
76         remove (fileName);
77         TiledRgbaOutputFile out (fileName, header, channels,
78                                  xSize, ySize, ONE_LEVEL);
79 
80         fillPixels (p1, width, height);
81         out.setFrameBuffer (&p1[0][0], 1, width);
82         out.writeTiles (0, out.numXTiles() - 1, 0, out.numYTiles() - 1);
83     }
84 
85     {
86         cout << " reading" << flush;
87 
88         TiledRgbaInputFile in (fileName);
89         const Box2i &dw = in.dataWindow();
90 
91         int w = dw.max.x - dw.min.x + 1;
92         int h = dw.max.y - dw.min.y + 1;
93         int dwx = dw.min.x;
94         int dwy = dw.min.y;
95 
96         Array2D<Rgba> p2 (h, w);
97         in.setFrameBuffer (&p2[-dwy][-dwx], 1, w);
98         in.readTiles (0, in.numXTiles() - 1, 0, in.numYTiles() - 1);
99 
100         cout << " comparing" << endl << flush;
101 
102         assert (in.displayWindow() == header.displayWindow());
103         assert (in.dataWindow() == header.dataWindow());
104         assert (in.pixelAspectRatio() == header.pixelAspectRatio());
105         assert (in.screenWindowCenter() == header.screenWindowCenter());
106         assert (in.screenWindowWidth() == header.screenWindowWidth());
107         assert (in.lineOrder() == header.lineOrder());
108         assert (in.compression() == header.compression());
109         assert (in.channels() == channels);
110 
111 	if (comp == B44_COMPRESSION ||
112             comp == B44A_COMPRESSION ||
113 	    comp == DWAA_COMPRESSION ||
114             comp == DWAB_COMPRESSION)
115 	{
116 	    for (int y = 0; y < h; y += ySize)
117 	    {
118 		for (int x = 0; x < w; x += xSize)
119 		{
120 		    int nx = min (w - x, xSize);
121 		    int ny = min (h - y, ySize);
122 
123 		    Array2D<Rgba> p3 (ny, nx);
124 		    Array2D<Rgba> p4 (ny, nx);
125 
126 		    for (int y1 = 0; y1 < ny; ++y1)
127 		    {
128 			for (int x1 = 0; x1 < nx; ++x1)
129 			{
130 			    p3[y1][x1] = p1[y + y1][x + x1];
131 			    p4[y1][x1] = p2[y + y1][x + x1];
132 			}
133 		    }
134 
135                     if (comp == B44_COMPRESSION ||
136                         comp == B44A_COMPRESSION)
137                     {
138                         compareB44 (nx, ny, p3, p4, channels);
139                     }
140 		    else if (comp == DWAA_COMPRESSION ||
141                              comp == DWAB_COMPRESSION)
142                     {
143 		        compareDwa (nx, ny, p3, p4, channels);
144                     }
145 		}
146 	    }
147 	}
148 	else
149 	{
150 	    for (int y = 0; y < h; ++y)
151 	    {
152 		for (int x = 0; x < w; ++x)
153 		{
154 		    if (channels & WRITE_R)
155 			assert (p2[y][x].r == p1[y][x].r);
156 		    else
157 			assert (p2[y][x].r == 0);
158 
159 		    if (channels & WRITE_G)
160 			assert (p2[y][x].g == p1[y][x].g);
161 		    else
162 			assert (p2[y][x].g == 0);
163 
164 		    if (channels & WRITE_B)
165 			assert (p2[y][x].b == p1[y][x].b);
166 		    else
167 			assert (p2[y][x].b == 0);
168 
169 		    if (channels & WRITE_A)
170 			assert (p2[y][x].a == p1[y][x].a);
171 		    else
172 			assert (p2[y][x].a == 1);
173 		}
174 	    }
175 	}
176     }
177 
178     remove (fileName);
179 }
180 
181 
182 void
writeReadRGBAMIP(const char fileName[],int width,int height,RgbaChannels channels,Compression comp,int xSize,int ySize)183 writeReadRGBAMIP (const char fileName[],
184 	          int width,
185 		  int height,
186 		  RgbaChannels channels,
187 		  Compression comp,
188 		  int xSize, int ySize)
189 {
190     cout << "levelMode 1" <<
191             ", compression " << comp <<
192             ", tileSize " << xSize << "x" << ySize << endl;
193 
194     Header header (width, height);
195     header.lineOrder() = INCREASING_Y;
196     header.compression() = comp;
197 
198     Array < Array2D<Rgba> > levels;
199 
200     {
201         cout << " writing" << flush;
202 
203         remove (fileName);
204         TiledRgbaOutputFile out (fileName, header, channels,
205                                  xSize, ySize, MIPMAP_LEVELS, ROUND_DOWN);
206 
207         int numLevels = out.numLevels();
208 	levels.resizeErase (numLevels);
209 
210         for (int level = 0; level < out.numLevels(); ++level)
211         {
212             int levelWidth  = out.levelWidth(level);
213             int levelHeight = out.levelHeight(level);
214             levels[level].resizeErase(levelHeight, levelWidth);
215             fillPixels (levels[level], levelWidth, levelHeight);
216 
217             out.setFrameBuffer (&(levels[level])[0][0], 1, levelWidth);
218             out.writeTiles (0, out.numXTiles(level) - 1,
219                             0, out.numYTiles(level) - 1, level);
220         }
221     }
222 
223     {
224         cout << " reading" << flush;
225 
226         TiledRgbaInputFile in (fileName);
227         const Box2i &dw = in.dataWindow();
228         int dwx = dw.min.x;
229         int dwy = dw.min.y;
230 
231         int numLevels = in.numLevels();
232         Array < Array2D<Rgba> > levels2 (numLevels);
233 
234         for (int level = 0; level < numLevels; ++level)
235         {
236             int levelWidth = in.levelWidth(level);
237             int levelHeight = in.levelHeight(level);
238             levels2[level].resizeErase(levelHeight, levelWidth);
239 
240             in.setFrameBuffer (&(levels2[level])[-dwy][-dwx], 1, levelWidth);
241             in.readTiles (0, in.numXTiles(level) - 1,
242                           0, in.numYTiles(level) - 1, level);
243         }
244 
245         cout << " comparing" << endl << flush;
246 
247         assert (in.displayWindow() == header.displayWindow());
248         assert (in.dataWindow() == header.dataWindow());
249         assert (in.pixelAspectRatio() == header.pixelAspectRatio());
250         assert (in.screenWindowCenter() == header.screenWindowCenter());
251         assert (in.screenWindowWidth() == header.screenWindowWidth());
252         assert (in.lineOrder() == header.lineOrder());
253         assert (in.compression() == header.compression());
254         assert (in.channels() == channels);
255 
256         for (int l = 0; l < numLevels; ++l)
257         {
258             for (int y = 0; y < in.levelHeight(l); ++y)
259             {
260                 for (int x = 0; x < in.levelWidth(l); ++x)
261                 {
262                     if (channels & WRITE_R)
263                         assert ((levels2[l])[y][x].r == (levels[l])[y][x].r);
264                     else
265                         assert ((levels2[l])[y][x].r == 0);
266 
267                     if (channels & WRITE_G)
268                         assert ((levels2[l])[y][x].g == (levels[l])[y][x].g);
269                     else
270                         assert ((levels2[l])[y][x].g == 0);
271 
272                     if (channels & WRITE_B)
273                         assert ((levels2[l])[y][x].b == (levels[l])[y][x].b);
274                     else
275                         assert ((levels2[l])[y][x].b == 0);
276 
277                     if (channels & WRITE_A)
278                         assert ((levels2[l])[y][x].a == (levels[l])[y][x].a);
279                     else
280                         assert ((levels2[l])[y][x].a == 1);
281                 }
282             }
283         }
284     }
285 
286     remove (fileName);
287 }
288 
289 
290 void
writeReadRGBARIP(const char fileName[],int width,int height,RgbaChannels channels,Compression comp,int xSize,int ySize)291 writeReadRGBARIP (const char fileName[],
292 	          int width,
293 		  int height,
294 		  RgbaChannels channels,
295 		  Compression comp,
296 		  int xSize, int ySize)
297 {
298     cout << "levelMode 2" <<
299             ", compression " << comp <<
300             ", tileSize " << xSize << "x" << ySize << endl;
301 
302     Header header (width, height);
303     header.lineOrder() = INCREASING_Y;
304     header.compression() = comp;
305 
306     Array2D < Array2D<Rgba> > levels;
307 
308     {
309         cout << " writing" << flush;
310 
311         remove (fileName);
312         TiledRgbaOutputFile out (fileName, header, channels,
313                                  xSize, ySize, RIPMAP_LEVELS, ROUND_UP);
314 
315 	levels.resizeErase (out.numYLevels(), out.numXLevels());
316 
317         for (int ylevel = 0; ylevel < out.numYLevels(); ++ylevel)
318         {
319             for (int xlevel = 0; xlevel < out.numXLevels(); ++xlevel)
320             {
321                 int levelWidth = out.levelWidth(xlevel);
322                 int levelHeight = out.levelHeight(ylevel);
323                 levels[ylevel][xlevel].resizeErase(levelHeight, levelWidth);
324                 fillPixels (levels[ylevel][xlevel], levelWidth, levelHeight);
325 
326                 out.setFrameBuffer (&(levels[ylevel][xlevel])[0][0], 1,
327                                     levelWidth);
328                 out.writeTiles (0, out.numXTiles(xlevel) - 1,
329                                 0, out.numYTiles(ylevel) - 1, xlevel, ylevel);
330             }
331         }
332     }
333 
334     {
335         cout << " reading" << flush;
336 
337         TiledRgbaInputFile in (fileName);
338         const Box2i &dw = in.dataWindow();
339         int dwx = dw.min.x;
340         int dwy = dw.min.y;
341 
342         int numXLevels = in.numXLevels();
343         int numYLevels = in.numYLevels();
344 	Array2D < Array2D<Rgba> > levels2 (numYLevels, numXLevels);
345 
346         for (int ylevel = 0; ylevel < numYLevels; ++ylevel)
347         {
348             for (int xlevel = 0; xlevel < numXLevels; ++xlevel)
349             {
350                 int levelWidth  = in.levelWidth(xlevel);
351                 int levelHeight = in.levelHeight(ylevel);
352                 levels2[ylevel][xlevel].resizeErase(levelHeight, levelWidth);
353                 in.setFrameBuffer (&(levels2[ylevel][xlevel])[-dwy][-dwx], 1,
354                                    levelWidth);
355 
356                 in.readTiles (0, in.numXTiles(xlevel) - 1,
357                               0, in.numYTiles(ylevel) - 1, xlevel, ylevel);
358             }
359         }
360 
361         cout << " comparing" << endl << flush;
362 
363         assert (in.displayWindow() == header.displayWindow());
364         assert (in.dataWindow() == header.dataWindow());
365         assert (in.pixelAspectRatio() == header.pixelAspectRatio());
366         assert (in.screenWindowCenter() == header.screenWindowCenter());
367         assert (in.screenWindowWidth() == header.screenWindowWidth());
368         assert (in.lineOrder() == header.lineOrder());
369         assert (in.compression() == header.compression());
370         assert (in.channels() == channels);
371 
372         for (int ly = 0; ly < numYLevels; ++ly)
373         {
374             for (int lx = 0; lx < numXLevels; ++lx)
375             {
376                 for (int y = 0; y < in.levelHeight(ly); ++y)
377                 {
378                     for (int x = 0; x < in.levelWidth(lx); ++x)
379                     {
380                         if (channels & WRITE_R)
381                             assert ((levels2[ly][lx])[y][x].r ==
382                                     (levels[ly][lx])[y][x].r);
383                         else
384                             assert ((levels2[ly][lx])[y][x].r == 0);
385 
386                         if (channels & WRITE_G)
387                             assert ((levels2[ly][lx])[y][x].g ==
388                                     (levels[ly][lx])[y][x].g);
389                         else
390                             assert ((levels2[ly][lx])[y][x].g == 0);
391 
392                         if (channels & WRITE_B)
393                             assert ((levels2[ly][lx])[y][x].b ==
394                                     (levels[ly][lx])[y][x].b);
395                         else
396                             assert ((levels2[ly][lx])[y][x].b == 0);
397 
398                         if (channels & WRITE_A)
399                             assert ((levels2[ly][lx])[y][x].a ==
400                                     (levels[ly][lx])[y][x].a);
401                         else
402                             assert ((levels2[ly][lx])[y][x].a == 1);
403                     }
404                 }
405             }
406         }
407     }
408 
409     remove (fileName);
410 }
411 
412 
413 void
writeRead(const std::string & tempDir,int W,int H,Compression comp,int xSize,int ySize)414 writeRead (const std::string &tempDir,
415            int W,
416            int H,
417            Compression comp,
418            int xSize,
419            int ySize)
420 {
421     std::string filename = tempDir + "imf_test_tiled_rgba.exr";
422 
423     writeReadRGBAONE (filename.c_str(), W, H, WRITE_RGBA, comp, xSize, ySize);
424 
425     if (comp != B44_COMPRESSION &&
426         comp != B44A_COMPRESSION &&
427         comp != DWAA_COMPRESSION &&
428         comp != DWAB_COMPRESSION)
429     {
430 	//
431 	// Skip mipmaps and ripmaps with B44 or DWA compression; writing
432 	// an image with a single resolution level, above, should be enough
433         // to verify that B44 and DWA compression work with tiled files.
434 	//
435 
436 	writeReadRGBAMIP
437             (filename.c_str(), W, H, WRITE_RGBA, comp, xSize, ySize);
438 
439 	writeReadRGBARIP
440             (filename.c_str(), W, H, WRITE_RGBA, comp, xSize, ySize);
441     }
442 }
443 
444 
445 void
writeReadIncomplete(const std::string & tempDir)446 writeReadIncomplete (const std::string &tempDir)
447 {
448     cout << "\nfile with missing and broken tiles" << endl;
449 
450     std::string fileName = tempDir + "imf_test_tiled_incomplete.exr";
451 
452     //
453     // Write a file where every other tile is missing or broken.
454     // Then try read the file and verify that all existing good
455     // tiles can actually be read.
456     //
457 
458     const int width = 400;
459     const int height = 300;
460     const int tileXSize = 30;
461     const int tileYSize = 40;
462 
463     Array2D<Rgba> p1 (height, width);
464 
465     for (int y = 0; y < height; ++y)
466 	for (int x = 0; x < width; ++x)
467 	    p1[y][x] = Rgba (x % 5, x % 17, y % 23, y % 29);
468 
469     {
470         cout << "writing" << endl;
471 
472         remove (fileName.c_str());
473 
474 	Header header (width, height);
475 	header.lineOrder() = RANDOM_Y;
476 
477         TiledRgbaOutputFile out (fileName.c_str(), header, WRITE_RGBA,
478                                  tileXSize, tileYSize, ONE_LEVEL);
479 
480         out.setFrameBuffer (&p1[0][0], 1, width);
481 
482 	out.writeTile (0, 0);
483 
484 	for (int tileY = 0; tileY < out.numYTiles(); ++tileY)
485 	    for (int tileX = 0; tileX < out.numXTiles(); ++tileX)
486 		if ((tileX + tileY) & 1)
487 		    out.writeTile (tileX, tileY);
488 
489 	out.writeTile (2, 0);
490 
491 	out.breakTile (0, 0, 0, 0, 25, 10, 0xff);	// destroy tiles
492 	out.breakTile (2, 0, 0, 0, 25, 10, 0xff);	// (0,0) and (2,0)
493     }
494 
495     {
496 	Array2D<Rgba> p2 (height, width);
497 
498 	for (int y = 0; y < height; ++y)
499 	    for (int x = 0; x < width; ++x)
500 		p2[y][x] = Rgba (-1, -1, -1, -1);
501 
502         cout << "reading one tile at a time," << flush;
503 
504         TiledRgbaInputFile in (fileName.c_str());
505         const Box2i &dw = in.dataWindow();
506 
507         assert (dw.max.x - dw.min.x + 1 == width);
508         assert (dw.max.y - dw.min.y + 1 == height);
509 	assert (dw.min.x == 0);
510 	assert (dw.min.y == 0);
511 
512         in.setFrameBuffer (&p2[0][0], 1, width);
513 
514 	for (int tileY = 0; tileY < in.numYTiles(); ++tileY)
515 	{
516 	    for (int tileX = 0; tileX < in.numXTiles(); ++tileX)
517 	    {
518 		bool tilePresent = true;
519 		bool tileBroken = false;
520 
521 		try
522 		{
523 		    in.readTile (tileX, tileY);
524 		}
525 		catch (const IEX_NAMESPACE::InputExc &)
526 		{
527 		    tilePresent = false;	// tile is missing
528 		}
529 		catch (const IEX_NAMESPACE::IoExc &)
530 		{
531 		    tileBroken = true;		// tile cannot be decoded
532 		}
533 
534 		assert (tileBroken || (tilePresent == ((tileX + tileY) & 1)));
535 	    }
536 	}
537 
538 	cout << " comparing" << endl << flush;
539 
540 	for (int y = 0; y < height; ++y)
541 	{
542 	    int tileY = y / tileYSize;
543 
544 	    for (int x = 0; x < width; ++x)
545 	    {
546 		int tileX = x / tileXSize;
547 
548 		const Rgba &s = p1[y][x];
549 		const Rgba &t = p2[y][x];
550 
551 		if ((tileX + tileY) & 1)
552 		{
553 		    assert (t.r == s.r &&
554 		            t.g == s.g &&
555 			    t.b == s.b &&
556 			    t.a == s.a);
557 		}
558 		else
559 		{
560 		    assert (t.r == -1 &&
561 			    t.g == -1 &&
562 			    t.b == -1 &&
563 			    t.a == -1);
564 		}
565 	    }
566 	}
567     }
568 
569     {
570 	Array2D<Rgba> p2 (height, width);
571 
572 	for (int y = 0; y < height; ++y)
573 	    for (int x = 0; x < width; ++x)
574 		p2[y][x] = Rgba (-1, -1, -1, -1);
575 
576         cout << "reading multiple tiles at a time," << flush;
577 
578         TiledRgbaInputFile in (fileName.c_str());
579         const Box2i &dw = in.dataWindow();
580 
581         assert (dw.max.x - dw.min.x + 1 == width);
582         assert (dw.max.y - dw.min.y + 1 == height);
583 	assert (dw.min.x == 0);
584 	assert (dw.min.y == 0);
585 
586         in.setFrameBuffer (&p2[0][0], 1, width);
587 
588 	for (int tileY = 0; tileY < in.numYTiles(); ++tileY)
589 	{
590 	    bool tilesMissing = false;
591 	    bool tilesBroken = false;
592 
593 	    try
594 	    {
595 		in.readTiles (0, in.numXTiles() - 1, tileY, tileY);
596 	    }
597 	    catch (const IEX_NAMESPACE::InputExc &)
598 	    {
599 		tilesMissing = true;
600 	    }
601 	    catch (const IEX_NAMESPACE::IoExc &)
602 	    {
603 		tilesBroken = true;
604 	    }
605 
606 	    assert (tilesMissing || tilesBroken);
607 	}
608 
609 	cout << " comparing" << endl << flush;
610 
611 	for (int y = 0; y < height; ++y)
612 	{
613 	    for (int x = 0; x < width; ++x)
614 	    {
615 		const Rgba &s = p1[y][x];
616 		const Rgba &t = p2[y][x];
617 
618 		assert ((t.r == -1  && t.g == -1  && t.b == -1  && t.a == -1) ||
619 			(t.r == s.r && t.g == s.g && t.b == s.b && t.a == s.a));
620 	    }
621 	}
622     }
623 
624     remove (fileName.c_str());
625 }
626 
627 
628 void
writeReadLayers(const std::string & tempDir)629 writeReadLayers(const std::string &tempDir)
630 {
631     cout << "\nreading multi-layer file" << endl;
632 
633     std::string fileName = tempDir + "imf_test_tiled_multi_layer_rgba.exr";
634 
635     const int W = 237;
636     const int H = 119;
637 
638     Array2D<half> p1 (H, W);
639     Array2D<half> p2 (H, W);
640 
641     for (int y = 0; y < H; ++y)
642     {
643 	for (int x = 0; x < W; ++x)
644 	{
645 	    p1[y][x] = half (y % 23 + x % 17);
646 	    p2[y][x] = half (y % 29 + x % 19);
647 	}
648     }
649 
650     {
651 	Header hdr (W, H);
652 	hdr.setTileDescription (TileDescription());
653 	hdr.channels().insert ("R", Channel (HALF));
654 	hdr.channels().insert ("foo.R", Channel (HALF));
655 
656 	FrameBuffer fb;
657 
658 	fb.insert ("R",
659 		   Slice (HALF,			// type
660 			  (char *) &p1[0][0], 	// base
661 			  sizeof (half),	// xStride
662 			  sizeof (half) * W));	// yStride
663 
664 	fb.insert ("foo.R",
665 		   Slice (HALF,			// type
666 			  (char *) &p2[0][0], 	// base
667 			  sizeof (half),	// xStride
668 			  sizeof (half) * W));	// yStride
669 
670 	TiledOutputFile out (fileName.c_str(), hdr);
671 	out.setFrameBuffer (fb);
672 	out.writeTiles (0, out.numXTiles() - 1, 0, out.numYTiles() - 1);
673     }
674 
675     {
676 	TiledRgbaInputFile in (fileName.c_str(), "");
677 
678 	Array2D<Rgba> p3 (H, W);
679 	in.setFrameBuffer (&p3[0][0], 1, W);
680 	in.readTiles (0, in.numXTiles() - 1, 0, in.numYTiles() - 1);
681 
682 	for (int y = 0; y < H; ++y)
683 	{
684 	    for (int x = 0; x < W; ++x)
685 	    {
686 		assert (p3[y][x].r == p1[y][x]);
687 		assert (p3[y][x].g == 0);
688 		assert (p3[y][x].b == 0);
689 		assert (p3[y][x].a == 1);
690 	    }
691 	}
692     }
693 
694     {
695 	TiledRgbaInputFile in (fileName.c_str(), "foo");
696 
697 	Array2D<Rgba> p3 (H, W);
698 	in.setFrameBuffer (&p3[0][0], 1, W);
699 	in.readTiles (0, in.numXTiles() - 1, 0, in.numYTiles() - 1);
700 
701 	for (int y = 0; y < H; ++y)
702 	{
703 	    for (int x = 0; x < W; ++x)
704 	    {
705 		assert (p3[y][x].r == p2[y][x]);
706 		assert (p3[y][x].g == 0);
707 		assert (p3[y][x].b == 0);
708 		assert (p3[y][x].a == 1);
709 	    }
710 	}
711     }
712 
713     {
714 	TiledRgbaInputFile in (fileName.c_str(), "");
715 
716 	Array2D<Rgba> p3 (H, W);
717 
718 	in.setFrameBuffer (&p3[0][0], 1, W);
719 
720 	in.readTiles (0, in.numXTiles() - 1,
721 		      0, in.numYTiles() / 2 - 1);
722 
723 	in.setLayerName ("foo");
724 
725 	in.setFrameBuffer (&p3[0][0], 1, W);
726 
727 	in.readTiles (0, in.numXTiles() - 1,
728 		      in.numYTiles() / 2, in.numYTiles() - 1);
729 
730 	for (int y = 0; y < H; ++y)
731 	{
732 	    for (int x = 0; x < W; ++x)
733 	    {
734 		if (y < static_cast<int>( (in.numYTiles() / 2) * in.tileYSize()))
735 		    assert (p3[y][x].r == p1[y][x]);
736 		else
737 		    assert (p3[y][x].r == p2[y][x]);
738 
739 		assert (p3[y][x].g == 0);
740 		assert (p3[y][x].b == 0);
741 		assert (p3[y][x].a == 1);
742 	    }
743 	}
744     }
745 
746     {
747 	Header hdr (W, H);
748 	hdr.setTileDescription (TileDescription());
749 	hdr.channels().insert ("Y", Channel (HALF));
750 	hdr.channels().insert ("foo.Y", Channel (HALF));
751 
752 	FrameBuffer fb;
753 
754 	fb.insert ("Y",
755 		   Slice (HALF,			// type
756 			  (char *) &p1[0][0], 	// base
757 			  sizeof (half),	// xStride
758 			  sizeof (half) * W));	// yStride
759 
760 	fb.insert ("foo.Y",
761 		   Slice (HALF,			// type
762 			  (char *) &p2[0][0], 	// base
763 			  sizeof (half),	// xStride
764 			  sizeof (half) * W));	// yStride
765 
766 	TiledOutputFile out (fileName.c_str(), hdr);
767 	out.setFrameBuffer (fb);
768 	out.writeTiles (0, out.numXTiles() - 1, 0, out.numYTiles() - 1);
769     }
770 
771     {
772 	TiledRgbaInputFile in (fileName.c_str(), "");
773 
774 	Array2D<Rgba> p3 (H, W);
775 	in.setFrameBuffer (&p3[0][0], 1, W);
776 	in.readTiles (0, in.numXTiles() - 1, 0, in.numYTiles() - 1);
777 
778 	for (int y = 0; y < H; ++y)
779 	{
780 	    for (int x = 0; x < W; ++x)
781 	    {
782 		assert (p3[y][x].r == p1[y][x]);
783 		assert (p3[y][x].g == p1[y][x]);
784 		assert (p3[y][x].b == p1[y][x]);
785 		assert (p3[y][x].a == 1);
786 	    }
787 	}
788     }
789 
790     {
791 	TiledRgbaInputFile in (fileName.c_str(), "foo");
792 
793 	Array2D<Rgba> p3 (H, W);
794 	in.setFrameBuffer (&p3[0][0], 1, W);
795 	in.readTiles (0, in.numXTiles() - 1, 0, in.numYTiles() - 1);
796 
797 	for (int y = 0; y < H; ++y)
798 	{
799 	    for (int x = 0; x < W; ++x)
800 	    {
801 		assert (p3[y][x].r == p2[y][x]);
802 		assert (p3[y][x].g == p2[y][x]);
803 		assert (p3[y][x].b == p2[y][x]);
804 		assert (p3[y][x].a == 1);
805 	    }
806 	}
807     }
808 
809     {
810 	TiledRgbaInputFile in (fileName.c_str(), "");
811 
812 	Array2D<Rgba> p3 (H, W);
813 
814 	in.setFrameBuffer (&p3[0][0], 1, W);
815 
816 	in.readTiles (0, in.numXTiles() - 1,
817 		      0, in.numYTiles() / 2 - 1);
818 
819 	in.setLayerName ("foo");
820 
821 	in.setFrameBuffer (&p3[0][0], 1, W);
822 
823 	in.readTiles (0, in.numXTiles() - 1,
824 		      in.numYTiles() / 2, in.numYTiles() - 1);
825 
826 	for (int y = 0; y < H; ++y)
827 	{
828 	    for (int x = 0; x < W; ++x)
829 	    {
830 		if (y < static_cast<int>((in.numYTiles() / 2) * in.tileYSize()))
831 		{
832 		    assert (p3[y][x].r == p1[y][x]);
833 		    assert (p3[y][x].g == p1[y][x]);
834 		    assert (p3[y][x].b == p1[y][x]);
835 		}
836 		else
837 		{
838 		    assert (p3[y][x].r == p2[y][x]);
839 		    assert (p3[y][x].g == p2[y][x]);
840 		    assert (p3[y][x].b == p2[y][x]);
841 		}
842 
843 		assert (p3[y][x].a == 1);
844 	    }
845 	}
846     }
847 
848     remove (fileName.c_str());
849 }
850 
851 } // namespace
852 
853 
854 void
testTiledRgba(const std::string & tempDir)855 testTiledRgba (const std::string &tempDir)
856 {
857     try
858     {
859         cout << "Testing the tiled RGBA image interface" << endl;
860 
861 	int maxThreads = ILMTHREAD_NAMESPACE::supportsThreads()? 3: 0;
862 
863 	for (int n = 0; n <= maxThreads; ++n)
864 	{
865 	    if (ILMTHREAD_NAMESPACE::supportsThreads())
866 	    {
867 		setGlobalThreadCount (n);
868 		cout << "\nnumber of threads: " << globalThreadCount() << endl;
869 	    }
870 
871 	    const int W[] = { 9, 69, 75, 80 };
872 	    const int H[] = { 7, 50, 52, 55 };
873 
874 	    for (int i = 0; i < 4; ++i)
875 	    {
876 		cout << "\nImage size = " << W[i] << " x " << H[i] << endl;
877 
878 		for (int comp = 0; comp < NUM_COMPRESSION_METHODS; ++comp)
879 		{
880 		    //
881 		    // for tiled files, ZIPS and ZIP are the same thing
882 		    //
883 
884 		    if (comp == ZIP_COMPRESSION)
885 			comp++;
886 
887 		    if (i == 0)
888 		    {
889 			//
890 			// for single-pixel tiles, we don't gain anything
891 			// by testing multiple image sizes (and singe-pixel
892 			// tiles are rather slow anyway)
893 			//
894 
895 			writeRead (tempDir, W[i], H[i], Compression (comp), 1, 1);
896 		    }
897 
898 		    writeRead (tempDir, W[i], H[i], Compression (comp), 35, 26);
899 		    writeRead (tempDir, W[i], H[i], Compression (comp), 75, 52);
900 		    writeRead (tempDir, W[i], H[i], Compression (comp), 264, 129);
901 		}
902 	    }
903 
904 	    writeReadIncomplete (tempDir);
905 	}
906 
907 	writeReadLayers (tempDir);
908 
909         cout << "ok\n" << endl;
910     }
911     catch (const std::exception &e)
912     {
913         cerr << "ERROR -- caught exception: " << e.what() << endl;
914         assert (false);
915     }
916 }
917