1///////////////////////////////////////////////////////////////////////////
2//
3// Copyright (c) 2004, Industrial Light & Magic, a division of Lucas
4// Digital Ltd. LLC
5//
6// All rights reserved.
7//
8// Redistribution and use in source and binary forms, with or without
9// modification, are permitted provided that the following conditions are
10// met:
11// *       Redistributions of source code must retain the above copyright
12// notice, this list of conditions and the following disclaimer.
13// *       Redistributions in binary form must reproduce the above
14// copyright notice, this list of conditions and the following disclaimer
15// in the documentation and/or other materials provided with the
16// distribution.
17// *       Neither the name of Industrial Light & Magic nor the names of
18// its contributors may be used to endorse or promote products derived
19// from this software without specific prior written permission.
20//
21// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
24// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
25// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
26// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
27// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
28// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
29// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
30// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
31// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32//
33///////////////////////////////////////////////////////////////////////////
34
35//-----------------------------------------------------------------------------
36//
37//	class InputFile
38//
39//-----------------------------------------------------------------------------
40
41#include <ImfInputFile.h>
42#include <ImfScanLineInputFile.h>
43#include <ImfTiledInputFile.h>
44#include <ImfChannelList.h>
45#include <ImfMisc.h>
46#include <ImfStdIO.h>
47#include <ImfVersion.h>
48#include "ImathFun.h"
49#include "IlmThreadMutex.h"
50#include "Iex.h"
51#include "half.h"
52#include <fstream>
53#include <algorithm>
54
55
56namespace Imf {
57
58
59using Imath::Box2i;
60using Imath::divp;
61using Imath::modp;
62using IlmThread::Mutex;
63using IlmThread::Lock;
64
65
66//
67// Struct InputFile::Data stores things that will be
68// needed between calls to readPixels
69//
70
71struct InputFile::Data: public Mutex
72{
73    Header              header;
74    int                 version;
75    IStream *		is;
76    bool		deleteStream;
77
78    TiledInputFile *	tFile;
79    ScanLineInputFile *	sFile;
80
81    LineOrder		lineOrder;      // the file's lineorder
82    int			minY;           // data window's min y coord
83    int			maxY;           // data window's max x coord
84
85    FrameBuffer		tFileBuffer;
86    FrameBuffer *	cachedBuffer;
87
88    int			cachedTileY;
89    int                 offset;
90
91    int                 numThreads;
92
93     Data (bool del, int numThreads);
94    ~Data ();
95
96    void		deleteCachedBuffer();
97};
98
99
100InputFile::Data::Data (bool del, int numThreads):
101    is (0),
102    deleteStream (del),
103    tFile (0),
104    sFile (0),
105    cachedBuffer (0),
106    cachedTileY (-1),
107    numThreads (numThreads)
108{
109    // empty
110}
111
112
113InputFile::Data::~Data ()
114{
115    delete tFile;
116    delete sFile;
117
118    if (deleteStream)
119	delete is;
120
121    deleteCachedBuffer();
122}
123
124
125void
126InputFile::Data::deleteCachedBuffer()
127{
128    //
129    // Delete the cached frame buffer, and all memory
130    // allocated for the slices in the cached frameBuffer.
131    //
132
133    if (cachedBuffer)
134    {
135	for (FrameBuffer::Iterator k = cachedBuffer->begin();
136	     k != cachedBuffer->end();
137	     ++k)
138	{
139	    Slice &s = k.slice();
140
141	    switch (s.type)
142	    {
143	      case UINT:
144
145		delete [] (((unsigned int *)s.base) + offset);
146		break;
147
148	      case HALF:
149
150		delete [] ((half *)s.base + offset);
151		break;
152
153	      case FLOAT:
154
155		delete [] (((float *)s.base) + offset);
156		break;
157	    }
158	}
159
160	//
161	// delete the cached frame buffer
162	//
163
164	delete cachedBuffer;
165	cachedBuffer = 0;
166    }
167}
168
169
170namespace {
171
172void
173bufferedReadPixels (InputFile::Data* ifd, int scanLine1, int scanLine2)
174{
175    //
176    // bufferedReadPixels reads each row of tiles that intersect the
177    // scan-line range (scanLine1 to scanLine2). The previous row of
178    // tiles is cached in order to prevent redundent tile reads when
179    // accessing scanlines sequentially.
180    //
181
182    int minY = std::min (scanLine1, scanLine2);
183    int maxY = std::max (scanLine1, scanLine2);
184
185    if (minY < ifd->minY || maxY >  ifd->maxY)
186    {
187        throw Iex::ArgExc ("Tried to read scan line outside "
188			   "the image file's data window.");
189    }
190
191    //
192    // The minimum and maximum y tile coordinates that intersect this
193    // scanline range
194    //
195
196    int minDy = (minY - ifd->minY) / ifd->tFile->tileYSize();
197    int maxDy = (maxY - ifd->minY) / ifd->tFile->tileYSize();
198
199    //
200    // Figure out which one is first in the file so we can read without seeking
201    //
202
203    int yStart, yEnd, yStep;
204
205    if (ifd->lineOrder == DECREASING_Y)
206    {
207        yStart = maxDy;
208        yEnd = minDy - 1;
209        yStep = -1;
210    }
211    else
212    {
213        yStart = minDy;
214        yEnd = maxDy + 1;
215        yStep = 1;
216    }
217
218    //
219    // the number of pixels in a row of tiles
220    //
221
222    Box2i levelRange = ifd->tFile->dataWindowForLevel(0);
223
224    //
225    // Read the tiles into our temporary framebuffer and copy them into
226    // the user's buffer
227    //
228
229    for (int j = yStart; j != yEnd; j += yStep)
230    {
231        Box2i tileRange = ifd->tFile->dataWindowForTile (0, j, 0);
232
233        int minYThisRow = std::max (minY, tileRange.min.y);
234        int maxYThisRow = std::min (maxY, tileRange.max.y);
235
236        if (j != ifd->cachedTileY)
237        {
238            //
239            // We don't have any valid buffered info, so we need to read in
240            // from the file.
241            //
242
243            ifd->tFile->readTiles (0, ifd->tFile->numXTiles (0) - 1, j, j);
244            ifd->cachedTileY = j;
245        }
246
247        //
248        // Copy the data from our cached framebuffer into the user's
249        // framebuffer.
250        //
251
252        for (FrameBuffer::ConstIterator k = ifd->cachedBuffer->begin();
253             k != ifd->cachedBuffer->end();
254             ++k)
255        {
256            Slice fromSlice = k.slice();		// slice to write from
257            Slice toSlice = ifd->tFileBuffer[k.name()];	// slice to write to
258
259            char *fromPtr, *toPtr;
260            int size = pixelTypeSize (toSlice.type);
261
262	    int xStart = levelRange.min.x;
263	    int yStart = minYThisRow;
264
265	    while (modp (xStart, toSlice.xSampling) != 0)
266		++xStart;
267
268	    while (modp (yStart, toSlice.ySampling) != 0)
269		++yStart;
270
271            for (int y = yStart;
272		 y <= maxYThisRow;
273		 y += toSlice.ySampling)
274            {
275		//
276                // Set the pointers to the start of the y scanline in
277                // this row of tiles
278		//
279
280                fromPtr = fromSlice.base +
281                          (y - tileRange.min.y) * fromSlice.yStride +
282                          xStart * fromSlice.xStride;
283
284                toPtr = toSlice.base +
285                        divp (y, toSlice.ySampling) * toSlice.yStride +
286                        divp (xStart, toSlice.xSampling) * toSlice.xStride;
287
288		//
289                // Copy all pixels for the scanline in this row of tiles
290		//
291
292                for (int x = xStart;
293		     x <= levelRange.max.x;
294		     x += toSlice.xSampling)
295                {
296		    for (size_t i = 0; i < size; ++i)
297			toPtr[i] = fromPtr[i];
298
299		    fromPtr += fromSlice.xStride * toSlice.xSampling;
300		    toPtr += toSlice.xStride;
301                }
302            }
303        }
304    }
305}
306
307} // namespace
308
309
310
311InputFile::InputFile (const char fileName[], int numThreads):
312    _data (new Data (true, numThreads))
313{
314    try
315    {
316	_data->is = new StdIFStream (fileName);
317	initialize();
318    }
319    catch (Iex::BaseExc &e)
320    {
321	delete _data;
322
323        REPLACE_EXC (e, "Cannot read image file "
324			"\"" << fileName << "\". " << e);
325        throw;
326    }
327    catch (...)
328    {
329	delete _data;
330        throw;
331    }
332}
333
334
335InputFile::InputFile (IStream &is, int numThreads):
336    _data (new Data (false, numThreads))
337{
338    try
339    {
340	_data->is = &is;
341	initialize();
342    }
343    catch (Iex::BaseExc &e)
344    {
345	delete _data;
346
347        REPLACE_EXC (e, "Cannot read image file "
348			"\"" << is.fileName() << "\". " << e);
349        throw;
350    }
351    catch (...)
352    {
353	delete _data;
354        throw;
355    }
356}
357
358
359void
360InputFile::initialize ()
361{
362    _data->header.readFrom (*_data->is, _data->version);
363    _data->header.sanityCheck (isTiled (_data->version));
364
365    if (isTiled (_data->version))
366    {
367	_data->lineOrder = _data->header.lineOrder();
368
369	//
370	// Save the dataWindow information
371	//
372
373	const Box2i &dataWindow = _data->header.dataWindow();
374	_data->minY = dataWindow.min.y;
375	_data->maxY = dataWindow.max.y;
376
377	_data->tFile = new TiledInputFile (_data->header,
378					   _data->is,
379					   _data->version,
380                                           _data->numThreads);
381    }
382    else
383    {
384	_data->sFile = new ScanLineInputFile (_data->header,
385					      _data->is,
386                                              _data->numThreads);
387    }
388}
389
390
391InputFile::~InputFile ()
392{
393    delete _data;
394}
395
396
397const char *
398InputFile::fileName () const
399{
400    return _data->is->fileName();
401}
402
403
404const Header &
405InputFile::header () const
406{
407    return _data->header;
408}
409
410
411int
412InputFile::version () const
413{
414    return _data->version;
415}
416
417
418void
419InputFile::setFrameBuffer (const FrameBuffer &frameBuffer)
420{
421    if (isTiled (_data->version))
422    {
423	Lock lock (*_data);
424
425	//
426        // We must invalidate the cached buffer if the new frame
427	// buffer has a different set of channels than the old
428	// frame buffer, or if the type of a channel has changed.
429	//
430
431	const FrameBuffer &oldFrameBuffer = _data->tFileBuffer;
432
433	FrameBuffer::ConstIterator i = oldFrameBuffer.begin();
434	FrameBuffer::ConstIterator j = frameBuffer.begin();
435
436	while (i != oldFrameBuffer.end())
437	{
438	    if (strcmp (i.name(), j.name()) || i.slice().type != j.slice().type)
439		break;
440
441	    ++i;
442	    ++j;
443	}
444
445	if (i != oldFrameBuffer.end() || j != frameBuffer.end())
446        {
447	    //
448	    // Invalidate the cached buffer.
449	    //
450
451            _data->deleteCachedBuffer ();
452	    _data->cachedTileY = -1;
453
454	    //
455	    // Create new a cached frame buffer.  It can hold a single
456	    // row of tiles.  The cached buffer can be reused for each
457	    // row of tiles because we set the yTileCoords parameter of
458	    // each Slice to true.
459	    //
460
461	    const Box2i &dataWindow = _data->header.dataWindow();
462	    _data->cachedBuffer = new FrameBuffer();
463	    _data->offset = dataWindow.min.x;
464
465	    int tileRowSize = (dataWindow.max.x - dataWindow.min.x + 1) *
466			      _data->tFile->tileYSize();
467
468	    for (FrameBuffer::ConstIterator k = frameBuffer.begin();
469		 k != frameBuffer.end();
470		 ++k)
471	    {
472		Slice s = k.slice();
473
474		switch (s.type)
475		{
476		  case UINT:
477
478		    _data->cachedBuffer->insert
479			(k.name(),
480			 Slice (UINT,
481				(char *)(new unsigned int[tileRowSize] -
482					_data->offset),
483				sizeof (unsigned int),
484				sizeof (unsigned int) *
485				    _data->tFile->levelWidth(0),
486				1, 1,
487				s.fillValue,
488				false, true));
489		    break;
490
491		  case HALF:
492
493		    _data->cachedBuffer->insert
494			(k.name(),
495			 Slice (HALF,
496				(char *)(new half[tileRowSize] -
497					_data->offset),
498				sizeof (half),
499				sizeof (half) *
500				    _data->tFile->levelWidth(0),
501				1, 1,
502				s.fillValue,
503				false, true));
504		    break;
505
506		  case FLOAT:
507
508		    _data->cachedBuffer->insert
509			(k.name(),
510			 Slice (FLOAT,
511				(char *)(new float[tileRowSize] -
512					_data->offset),
513				sizeof(float),
514				sizeof(float) *
515				    _data->tFile->levelWidth(0),
516				1, 1,
517				s.fillValue,
518				false, true));
519		    break;
520
521		  default:
522
523		    throw Iex::ArgExc ("Unknown pixel data type.");
524		}
525	    }
526
527	    _data->tFile->setFrameBuffer (*_data->cachedBuffer);
528        }
529
530	_data->tFileBuffer = frameBuffer;
531    }
532    else
533    {
534        _data->sFile->setFrameBuffer (frameBuffer);
535    }
536}
537
538
539const FrameBuffer &
540InputFile::frameBuffer () const
541{
542    if (isTiled (_data->version))
543    {
544	Lock lock (*_data);
545	return _data->tFileBuffer;
546    }
547    else
548    {
549	return _data->sFile->frameBuffer();
550    }
551}
552
553
554bool
555InputFile::isComplete () const
556{
557    if (isTiled (_data->version))
558	return _data->tFile->isComplete();
559    else
560	return _data->sFile->isComplete();
561}
562
563
564void
565InputFile::readPixels (int scanLine1, int scanLine2)
566{
567    if (isTiled (_data->version))
568    {
569	Lock lock (*_data);
570        bufferedReadPixels (_data, scanLine1, scanLine2);
571    }
572    else
573    {
574        _data->sFile->readPixels (scanLine1, scanLine2);
575    }
576}
577
578
579void
580InputFile::readPixels (int scanLine)
581{
582    readPixels (scanLine, scanLine);
583}
584
585
586void
587InputFile::rawPixelData (int firstScanLine,
588			 const char *&pixelData,
589			 int &pixelDataSize)
590{
591    try
592    {
593	if (isTiled (_data->version))
594	{
595	    throw Iex::ArgExc ("Tried to read a raw scanline "
596			       "from a tiled image.");
597	}
598
599        _data->sFile->rawPixelData (firstScanLine, pixelData, pixelDataSize);
600    }
601    catch (Iex::BaseExc &e)
602    {
603	REPLACE_EXC (e, "Error reading pixel data from image "
604		        "file \"" << fileName() << "\". " << e);
605	throw;
606    }
607}
608
609
610void
611InputFile::rawTileData (int &dx, int &dy,
612			int &lx, int &ly,
613			const char *&pixelData,
614			int &pixelDataSize)
615{
616    try
617    {
618	if (!isTiled (_data->version))
619	{
620	    throw Iex::ArgExc ("Tried to read a raw tile "
621			       "from a scanline-based image.");
622	}
623
624        _data->tFile->rawTileData (dx, dy, lx, ly, pixelData, pixelDataSize);
625    }
626    catch (Iex::BaseExc &e)
627    {
628	REPLACE_EXC (e, "Error reading tile data from image "
629		        "file \"" << fileName() << "\". " << e);
630	throw;
631    }
632}
633
634
635TiledInputFile*
636InputFile::tFile()
637{
638    if (!isTiled (_data->version))
639    {
640	throw Iex::ArgExc ("Cannot get a TiledInputFile pointer "
641			   "from an InputFile that is not tiled.");
642    }
643
644    return _data->tFile;
645}
646
647
648} // namespace Imf
649