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 TiledInputFile
38 //
39 //-----------------------------------------------------------------------------
40
41 #include "ImfTiledInputFile.h"
42 #include "ImfTileDescriptionAttribute.h"
43 #include "ImfChannelList.h"
44 #include "ImfMisc.h"
45 #include "ImfTiledMisc.h"
46 #include "ImfStdIO.h"
47 #include "ImfCompressor.h"
48 #include "ImfXdr.h"
49 #include "ImfConvert.h"
50 #include "ImfVersion.h"
51 #include "ImfTileOffsets.h"
52 #include "ImfThreading.h"
53 #include "ImfPartType.h"
54 #include "ImfMultiPartInputFile.h"
55 #include "ImfInputStreamMutex.h"
56 #include "IlmThreadPool.h"
57 #include "IlmThreadSemaphore.h"
58 #include "IlmThreadMutex.h"
59 #include "ImathVec.h"
60 #include "Iex.h"
61 #include <string>
62 #include <vector>
63 #include <algorithm>
64 #include <assert.h>
65 #include "ImfInputPartData.h"
66 #include "ImfNamespace.h"
67
68 OPENEXR_IMF_INTERNAL_NAMESPACE_SOURCE_ENTER
69
70 using IMATH_NAMESPACE::Box2i;
71 using IMATH_NAMESPACE::V2i;
72 using std::string;
73 using std::vector;
74 using std::min;
75 using std::max;
76 using ILMTHREAD_NAMESPACE::Mutex;
77 using ILMTHREAD_NAMESPACE::Lock;
78 using ILMTHREAD_NAMESPACE::Semaphore;
79 using ILMTHREAD_NAMESPACE::Task;
80 using ILMTHREAD_NAMESPACE::TaskGroup;
81 using ILMTHREAD_NAMESPACE::ThreadPool;
82
83 namespace {
84
85 struct TInSliceInfo
86 {
87 PixelType typeInFrameBuffer;
88 PixelType typeInFile;
89 char * base;
90 size_t xStride;
91 size_t yStride;
92 bool fill;
93 bool skip;
94 double fillValue;
95 int xTileCoords;
96 int yTileCoords;
97
98 TInSliceInfo (PixelType typeInFrameBuffer = HALF,
99 PixelType typeInFile = HALF,
100 char *base = 0,
101 size_t xStride = 0,
102 size_t yStride = 0,
103 bool fill = false,
104 bool skip = false,
105 double fillValue = 0.0,
106 int xTileCoords = 0,
107 int yTileCoords = 0);
108 };
109
110
TInSliceInfo(PixelType tifb,PixelType tifl,char * b,size_t xs,size_t ys,bool f,bool s,double fv,int xtc,int ytc)111 TInSliceInfo::TInSliceInfo (PixelType tifb,
112 PixelType tifl,
113 char *b,
114 size_t xs, size_t ys,
115 bool f, bool s,
116 double fv,
117 int xtc,
118 int ytc)
119 :
120 typeInFrameBuffer (tifb),
121 typeInFile (tifl),
122 base (b),
123 xStride (xs),
124 yStride (ys),
125 fill (f),
126 skip (s),
127 fillValue (fv),
128 xTileCoords (xtc),
129 yTileCoords (ytc)
130 {
131 // empty
132 }
133
134
135 struct TileBuffer
136 {
137 const char * uncompressedData;
138 char * buffer;
139 int dataSize;
140 Compressor * compressor;
141 Compressor::Format format;
142 int dx;
143 int dy;
144 int lx;
145 int ly;
146 bool hasException;
147 string exception;
148
149 TileBuffer (Compressor * const comp);
150 ~TileBuffer ();
151
wait__anondf056c5e0111::TileBuffer152 inline void wait () {_sem.wait();}
post__anondf056c5e0111::TileBuffer153 inline void post () {_sem.post();}
154
155 protected:
156
157 Semaphore _sem;
158 };
159
160
TileBuffer(Compressor * comp)161 TileBuffer::TileBuffer (Compressor *comp):
162 uncompressedData (0),
163 buffer (0),
164 dataSize (0),
165 compressor (comp),
166 format (defaultFormat (compressor)),
167 dx (-1),
168 dy (-1),
169 lx (-1),
170 ly (-1),
171 hasException (false),
172 exception (),
173 _sem (1)
174 {
175 // empty
176 }
177
178
~TileBuffer()179 TileBuffer::~TileBuffer ()
180 {
181 delete compressor;
182 }
183
184 } // namespace
185
186
187 class MultiPartInputFile;
188
189
190 //
191 // struct TiledInputFile::Data stores things that will be
192 // needed between calls to readTile()
193 //
194
195 struct TiledInputFile::Data: public Mutex
196 {
197 Header header; // the image header
198 TileDescription tileDesc; // describes the tile layout
199 int version; // file's version
200 FrameBuffer frameBuffer; // framebuffer to write into
201 LineOrder lineOrder; // the file's lineorder
202 int minX; // data window's min x coord
203 int maxX; // data window's max x coord
204 int minY; // data window's min y coord
205 int maxY; // data window's max x coord
206
207 int numXLevels; // number of x levels
208 int numYLevels; // number of y levels
209 int * numXTiles; // number of x tiles at a level
210 int * numYTiles; // number of y tiles at a level
211
212 TileOffsets tileOffsets; // stores offsets in file for
213 // each tile
214
215 bool fileIsComplete; // True if no tiles are missing
216 // in the file
217
218 vector<TInSliceInfo> slices; // info about channels in file
219
220 size_t bytesPerPixel; // size of an uncompressed pixel
221
222 size_t maxBytesPerTileLine; // combined size of a line
223 // over all channels
224
225 int partNumber; // part number
226
227 bool multiPartBackwardSupport; // if we are reading a multipart file
228 // using OpenEXR 1.7 API
229
230 int numThreads; // number of threads
231
232 MultiPartInputFile* multiPartFile; // the MultiPartInputFile used to
233 // support backward compatibility
234
235 vector<TileBuffer*> tileBuffers; // each holds a single tile
236 size_t tileBufferSize; // size of the tile buffers
237
238 bool memoryMapped; // if the stream is memory mapped
239
240 InputStreamMutex * _streamData;
241 bool _deleteStream;
242
243 Data (int numThreads);
244 ~Data ();
245
246 inline TileBuffer * getTileBuffer (int number);
247 // hash function from tile indices
248 // into our vector of tile buffers
249 };
250
251
Data(int numThreads)252 TiledInputFile::Data::Data (int numThreads):
253 numXTiles (0),
254 numYTiles (0),
255 partNumber (-1),
256 multiPartBackwardSupport(false),
257 numThreads(numThreads),
258 memoryMapped(false),
259 _streamData(NULL),
260 _deleteStream(false)
261 {
262 //
263 // We need at least one tileBuffer, but if threading is used,
264 // to keep n threads busy we need 2*n tileBuffers
265 //
266
267 tileBuffers.resize (max (1, 2 * numThreads));
268 }
269
270
~Data()271 TiledInputFile::Data::~Data ()
272 {
273 delete [] numXTiles;
274 delete [] numYTiles;
275
276 for (size_t i = 0; i < tileBuffers.size(); i++)
277 delete tileBuffers[i];
278
279 if (multiPartBackwardSupport)
280 delete multiPartFile;
281 }
282
283
284 TileBuffer*
getTileBuffer(int number)285 TiledInputFile::Data::getTileBuffer (int number)
286 {
287 return tileBuffers[number % tileBuffers.size()];
288 }
289
290
291 namespace {
292
293 void
readTileData(InputStreamMutex * streamData,TiledInputFile::Data * ifd,int dx,int dy,int lx,int ly,char * & buffer,int & dataSize)294 readTileData (InputStreamMutex *streamData,
295 TiledInputFile::Data *ifd,
296 int dx, int dy,
297 int lx, int ly,
298 char *&buffer,
299 int &dataSize)
300 {
301 //
302 // Read a single tile block from the file and into the array pointed
303 // to by buffer. If the file is memory-mapped, then we change where
304 // buffer points instead of writing into the array (hence buffer needs
305 // to be a reference to a char *).
306 //
307
308 //
309 // Look up the location for this tile in the Index and
310 // seek to that position if necessary
311 //
312
313 Int64 tileOffset = ifd->tileOffsets (dx, dy, lx, ly);
314
315 if (tileOffset == 0)
316 {
317 THROW (IEX_NAMESPACE::InputExc, "Tile (" << dx << ", " << dy << ", " <<
318 lx << ", " << ly << ") is missing.");
319 }
320
321
322 //
323 // In a multi-part file, the next chunk does not need to
324 // belong to the same part, so we have to compare the
325 // offset here.
326 //
327
328 if (!isMultiPart(ifd->version))
329 {
330 if (streamData->currentPosition != tileOffset)
331 streamData->is->seekg (tileOffset);
332 }
333 else
334 {
335 //
336 // In a multi-part file, the file pointer may be moved by other
337 // parts, so we have to ask tellg() where we are.
338 //
339 if (streamData->is->tellg() != tileOffset)
340 streamData->is->seekg (tileOffset);
341 }
342
343 //
344 // Read the first few bytes of the tile (the header).
345 // Verify that the tile coordinates and the level number
346 // are correct.
347 //
348
349 int tileXCoord, tileYCoord, levelX, levelY;
350
351 if (isMultiPart(ifd->version))
352 {
353 int partNumber;
354 Xdr::read <StreamIO> (*streamData->is, partNumber);
355 if (partNumber != ifd->partNumber)
356 {
357 THROW (IEX_NAMESPACE::ArgExc, "Unexpected part number " << partNumber
358 << ", should be " << ifd->partNumber << ".");
359 }
360 }
361
362 OPENEXR_IMF_INTERNAL_NAMESPACE::Xdr::read <OPENEXR_IMF_INTERNAL_NAMESPACE::StreamIO> (*streamData->is, tileXCoord);
363 OPENEXR_IMF_INTERNAL_NAMESPACE::Xdr::read <OPENEXR_IMF_INTERNAL_NAMESPACE::StreamIO> (*streamData->is, tileYCoord);
364 OPENEXR_IMF_INTERNAL_NAMESPACE::Xdr::read <OPENEXR_IMF_INTERNAL_NAMESPACE::StreamIO> (*streamData->is, levelX);
365 OPENEXR_IMF_INTERNAL_NAMESPACE::Xdr::read <OPENEXR_IMF_INTERNAL_NAMESPACE::StreamIO> (*streamData->is, levelY);
366 OPENEXR_IMF_INTERNAL_NAMESPACE::Xdr::read <OPENEXR_IMF_INTERNAL_NAMESPACE::StreamIO> (*streamData->is, dataSize);
367
368 if (tileXCoord != dx)
369 throw IEX_NAMESPACE::InputExc ("Unexpected tile x coordinate.");
370
371 if (tileYCoord != dy)
372 throw IEX_NAMESPACE::InputExc ("Unexpected tile y coordinate.");
373
374 if (levelX != lx)
375 throw IEX_NAMESPACE::InputExc ("Unexpected tile x level number coordinate.");
376
377 if (levelY != ly)
378 throw IEX_NAMESPACE::InputExc ("Unexpected tile y level number coordinate.");
379
380 if (dataSize > (int) ifd->tileBufferSize)
381 throw IEX_NAMESPACE::InputExc ("Unexpected tile block length.");
382
383 //
384 // Read the pixel data.
385 //
386
387 if (streamData->is->isMemoryMapped ())
388 buffer = streamData->is->readMemoryMapped (dataSize);
389 else
390 streamData->is->read (buffer, dataSize);
391
392 //
393 // Keep track of which tile is the next one in
394 // the file, so that we can avoid redundant seekg()
395 // operations (seekg() can be fairly expensive).
396 //
397
398 streamData->currentPosition = tileOffset + 5 * Xdr::size<int>() + dataSize;
399 }
400
401
402 void
readNextTileData(InputStreamMutex * streamData,TiledInputFile::Data * ifd,int & dx,int & dy,int & lx,int & ly,char * & buffer,int & dataSize)403 readNextTileData (InputStreamMutex *streamData,
404 TiledInputFile::Data *ifd,
405 int &dx, int &dy,
406 int &lx, int &ly,
407 char * & buffer,
408 int &dataSize)
409 {
410 //
411 // Read the next tile block from the file
412 //
413
414 if(isMultiPart(ifd->version))
415 {
416 int part;
417 Xdr::read <StreamIO> (*streamData->is, part);
418 if(part!=ifd->partNumber)
419 {
420 throw IEX_NAMESPACE::InputExc("Unexpected part number in readNextTileData");
421 }
422 }
423
424 //
425 // Read the first few bytes of the tile (the header).
426 //
427
428 Xdr::read <StreamIO> (*streamData->is, dx);
429 Xdr::read <StreamIO> (*streamData->is, dy);
430 Xdr::read <StreamIO> (*streamData->is, lx);
431 Xdr::read <StreamIO> (*streamData->is, ly);
432 Xdr::read <StreamIO> (*streamData->is, dataSize);
433
434 if (dataSize > (int) ifd->tileBufferSize)
435 throw IEX_NAMESPACE::InputExc ("Unexpected tile block length.");
436
437 //
438 // Read the pixel data.
439 //
440
441 streamData->is->read (buffer, dataSize);
442
443 //
444 // Keep track of which tile is the next one in
445 // the file, so that we can avoid redundant seekg()
446 // operations (seekg() can be fairly expensive).
447 //
448
449 streamData->currentPosition += 5 * Xdr::size<int>() + dataSize;
450 }
451
452
453 //
454 // A TileBufferTask encapsulates the task of uncompressing
455 // a single tile and copying it into the frame buffer.
456 //
457
458 class TileBufferTask : public Task
459 {
460 public:
461
462 TileBufferTask (TaskGroup *group,
463 TiledInputFile::Data *ifd,
464 TileBuffer *tileBuffer);
465
466 virtual ~TileBufferTask ();
467
468 virtual void execute ();
469
470 private:
471
472 TiledInputFile::Data * _ifd;
473 TileBuffer * _tileBuffer;
474 };
475
476
TileBufferTask(TaskGroup * group,TiledInputFile::Data * ifd,TileBuffer * tileBuffer)477 TileBufferTask::TileBufferTask
478 (TaskGroup *group,
479 TiledInputFile::Data *ifd,
480 TileBuffer *tileBuffer)
481 :
482 Task (group),
483 _ifd (ifd),
484 _tileBuffer (tileBuffer)
485 {
486 // empty
487 }
488
489
~TileBufferTask()490 TileBufferTask::~TileBufferTask ()
491 {
492 //
493 // Signal that the tile buffer is now free
494 //
495
496 _tileBuffer->post ();
497 }
498
499
500 void
execute()501 TileBufferTask::execute ()
502 {
503 try
504 {
505 //
506 // Calculate information about the tile
507 //
508
509 Box2i tileRange = OPENEXR_IMF_INTERNAL_NAMESPACE::dataWindowForTile (
510 _ifd->tileDesc,
511 _ifd->minX, _ifd->maxX,
512 _ifd->minY, _ifd->maxY,
513 _tileBuffer->dx,
514 _tileBuffer->dy,
515 _tileBuffer->lx,
516 _tileBuffer->ly);
517
518 int numPixelsPerScanLine = tileRange.max.x - tileRange.min.x + 1;
519
520 int numPixelsInTile = numPixelsPerScanLine *
521 (tileRange.max.y - tileRange.min.y + 1);
522
523 int sizeOfTile = _ifd->bytesPerPixel * numPixelsInTile;
524
525
526 //
527 // Uncompress the data, if necessary
528 //
529
530 if (_tileBuffer->compressor && _tileBuffer->dataSize < sizeOfTile)
531 {
532 _tileBuffer->format = _tileBuffer->compressor->format();
533
534 _tileBuffer->dataSize = _tileBuffer->compressor->uncompressTile
535 (_tileBuffer->buffer, _tileBuffer->dataSize,
536 tileRange, _tileBuffer->uncompressedData);
537 }
538 else
539 {
540 //
541 // If the line is uncompressed, it's in XDR format,
542 // regardless of the compressor's output format.
543 //
544
545 _tileBuffer->format = Compressor::XDR;
546 _tileBuffer->uncompressedData = _tileBuffer->buffer;
547 }
548
549 //
550 // Convert the tile of pixel data back from the machine-independent
551 // representation, and store the result in the frame buffer.
552 //
553
554 const char *readPtr = _tileBuffer->uncompressedData;
555 // points to where we
556 // read from in the
557 // tile block
558
559 //
560 // Iterate over the scan lines in the tile.
561 //
562
563 for (int y = tileRange.min.y; y <= tileRange.max.y; ++y)
564 {
565 //
566 // Iterate over all image channels.
567 //
568
569 for (unsigned int i = 0; i < _ifd->slices.size(); ++i)
570 {
571 const TInSliceInfo &slice = _ifd->slices[i];
572
573 //
574 // These offsets are used to facilitate both
575 // absolute and tile-relative pixel coordinates.
576 //
577
578 int xOffset = slice.xTileCoords * tileRange.min.x;
579 int yOffset = slice.yTileCoords * tileRange.min.y;
580
581 //
582 // Fill the frame buffer with pixel data.
583 //
584
585 if (slice.skip)
586 {
587 //
588 // The file contains data for this channel, but
589 // the frame buffer contains no slice for this channel.
590 //
591
592 skipChannel (readPtr, slice.typeInFile,
593 numPixelsPerScanLine);
594 }
595 else
596 {
597 //
598 // The frame buffer contains a slice for this channel.
599 //
600
601 char *writePtr = slice.base +
602 (y - yOffset) * slice.yStride +
603 (tileRange.min.x - xOffset) *
604 slice.xStride;
605
606 char *endPtr = writePtr +
607 (numPixelsPerScanLine - 1) * slice.xStride;
608
609 copyIntoFrameBuffer (readPtr, writePtr, endPtr,
610 slice.xStride,
611 slice.fill, slice.fillValue,
612 _tileBuffer->format,
613 slice.typeInFrameBuffer,
614 slice.typeInFile);
615 }
616 }
617 }
618 }
619 catch (std::exception &e)
620 {
621 if (!_tileBuffer->hasException)
622 {
623 _tileBuffer->exception = e.what ();
624 _tileBuffer->hasException = true;
625 }
626 }
627 catch (...)
628 {
629 if (!_tileBuffer->hasException)
630 {
631 _tileBuffer->exception = "unrecognized exception";
632 _tileBuffer->hasException = true;
633 }
634 }
635 }
636
637
638 TileBufferTask *
newTileBufferTask(TaskGroup * group,InputStreamMutex * streamData,TiledInputFile::Data * ifd,int number,int dx,int dy,int lx,int ly)639 newTileBufferTask
640 (TaskGroup *group,
641 InputStreamMutex *streamData,
642 TiledInputFile::Data *ifd,
643 int number,
644 int dx, int dy,
645 int lx, int ly)
646 {
647 //
648 // Wait for a tile buffer to become available,
649 // fill the buffer with raw data from the file,
650 // and create a new TileBufferTask whose execute()
651 // method will uncompress the tile and copy the
652 // tile's pixels into the frame buffer.
653 //
654
655 TileBuffer *tileBuffer = ifd->getTileBuffer (number);
656
657 try
658 {
659 tileBuffer->wait();
660
661 tileBuffer->dx = dx;
662 tileBuffer->dy = dy;
663 tileBuffer->lx = lx;
664 tileBuffer->ly = ly;
665
666 tileBuffer->uncompressedData = 0;
667
668 readTileData (streamData, ifd, dx, dy, lx, ly,
669 tileBuffer->buffer,
670 tileBuffer->dataSize);
671 }
672 catch (...)
673 {
674 //
675 // Reading from the file caused an exception.
676 // Signal that the tile buffer is free, and
677 // re-throw the exception.
678 //
679
680 tileBuffer->post();
681 throw;
682 }
683
684 return new TileBufferTask (group, ifd, tileBuffer);
685 }
686
687
688 } // namespace
689
690
TiledInputFile(const char fileName[],int numThreads)691 TiledInputFile::TiledInputFile (const char fileName[], int numThreads):
692 _data (new Data (numThreads))
693 {
694 _data->_streamData=NULL;
695 _data->_deleteStream=true;
696
697 //
698 // This constructor is called when a user
699 // explicitly wants to read a tiled file.
700 //
701
702
703 IStream* is = 0;
704 try
705 {
706 is = new StdIFStream (fileName);
707 readMagicNumberAndVersionField(*is, _data->version);
708
709 //
710 // Backward compatibility to read multpart file.
711 //
712 if (isMultiPart(_data->version))
713 {
714 compatibilityInitialize(*is);
715 return;
716 }
717
718 _data->_streamData = new InputStreamMutex();
719 _data->_streamData->is = is;
720 _data->header.readFrom (*_data->_streamData->is, _data->version);
721 initialize();
722 //read tile offsets - we are not multipart or deep
723 _data->tileOffsets.readFrom (*(_data->_streamData->is), _data->fileIsComplete,false,false);
724 _data->_streamData->currentPosition = _data->_streamData->is->tellg();
725 }
726 catch (IEX_NAMESPACE::BaseExc &e)
727 {
728 if (_data->_streamData != 0)
729 {
730 if (_data->_streamData->is != 0)
731 {
732 delete _data->_streamData->is;
733 _data->_streamData->is = is = 0;
734 }
735
736 delete _data->_streamData;
737 }
738
739 if (is != 0)
740 delete is;
741
742 REPLACE_EXC (e, "Cannot open image file "
743 "\"" << fileName << "\". " << e);
744 throw;
745 }
746 catch (...)
747 {
748 if ( _data->_streamData != 0)
749 {
750 if ( _data->_streamData->is != 0)
751 {
752 delete _data->_streamData->is;
753 _data->_streamData->is = is = 0;
754 }
755
756 delete _data->_streamData;
757 }
758
759 if (is != 0)
760 delete is;
761 throw;
762 }
763 }
764
765
TiledInputFile(OPENEXR_IMF_INTERNAL_NAMESPACE::IStream & is,int numThreads)766 TiledInputFile::TiledInputFile (OPENEXR_IMF_INTERNAL_NAMESPACE::IStream &is, int numThreads):
767 _data (new Data (numThreads))
768 {
769 _data->_deleteStream=false;
770 //
771 // This constructor is called when a user
772 // explicitly wants to read a tiled file.
773 //
774
775 bool streamDataCreated = false;
776
777 try
778 {
779 readMagicNumberAndVersionField(is, _data->version);
780
781 //
782 // Backward compatibility to read multpart file.
783 //
784 if (isMultiPart(_data->version))
785 {
786 compatibilityInitialize(is);
787 return;
788 }
789
790 streamDataCreated = true;
791 _data->_streamData = new InputStreamMutex();
792 _data->_streamData->is = &is;
793 _data->header.readFrom (*_data->_streamData->is, _data->version);
794 initialize();
795 // file is guaranteed to be single part, regular image
796 _data->tileOffsets.readFrom (*(_data->_streamData->is), _data->fileIsComplete,false,false);
797 _data->memoryMapped = _data->_streamData->is->isMemoryMapped();
798 _data->_streamData->currentPosition = _data->_streamData->is->tellg();
799 }
800 catch (IEX_NAMESPACE::BaseExc &e)
801 {
802 if (streamDataCreated) delete _data->_streamData;
803 delete _data;
804
805 REPLACE_EXC (e, "Cannot open image file "
806 "\"" << is.fileName() << "\". " << e);
807 throw;
808 }
809 catch (...)
810 {
811 if (streamDataCreated) delete _data->_streamData;
812 delete _data;
813 throw;
814 }
815 }
816
817
TiledInputFile(const Header & header,OPENEXR_IMF_INTERNAL_NAMESPACE::IStream * is,int version,int numThreads)818 TiledInputFile::TiledInputFile (const Header &header,
819 OPENEXR_IMF_INTERNAL_NAMESPACE::IStream *is,
820 int version,
821 int numThreads) :
822 _data (new Data (numThreads))
823 {
824 _data->_deleteStream=false;
825 _data->_streamData = new InputStreamMutex();
826 //
827 // This constructor called by class Imf::InputFile
828 // when a user wants to just read an image file, and
829 // doesn't care or know if the file is tiled.
830 // No need to have backward compatibility here, because
831 // we have somehow got the header.
832 //
833
834 _data->_streamData->is = is;
835 _data->header = header;
836 _data->version = version;
837 initialize();
838 _data->tileOffsets.readFrom (*(_data->_streamData->is),_data->fileIsComplete,false,false);
839 _data->memoryMapped = is->isMemoryMapped();
840 _data->_streamData->currentPosition = _data->_streamData->is->tellg();
841 }
842
843
TiledInputFile(InputPartData * part)844 TiledInputFile::TiledInputFile (InputPartData* part)
845 {
846 _data = new Data (part->numThreads);
847 _data->_deleteStream=false;
848 multiPartInitialize(part);
849 }
850
851
852 void
compatibilityInitialize(OPENEXR_IMF_INTERNAL_NAMESPACE::IStream & is)853 TiledInputFile::compatibilityInitialize(OPENEXR_IMF_INTERNAL_NAMESPACE::IStream& is)
854 {
855 is.seekg(0);
856 //
857 // Construct a MultiPartInputFile, initialize TiledInputFile
858 // with the part 0 data.
859 // (TODO) maybe change the third parameter of the constructor of MultiPartInputFile later.
860 //
861 _data->multiPartBackwardSupport = true;
862 _data->multiPartFile = new MultiPartInputFile(is, _data->numThreads);
863 InputPartData* part = _data->multiPartFile->getPart(0);
864
865 multiPartInitialize(part);
866 }
867
868
869 void
multiPartInitialize(InputPartData * part)870 TiledInputFile::multiPartInitialize(InputPartData* part)
871 {
872 if (part->header.type() != TILEDIMAGE)
873 throw IEX_NAMESPACE::ArgExc("Can't build a TiledInputFile from a type-mismatched part.");
874
875 _data->_streamData = part->mutex;
876 _data->header = part->header;
877 _data->version = part->version;
878 _data->partNumber = part->partNumber;
879 _data->memoryMapped = _data->_streamData->is->isMemoryMapped();
880 initialize();
881 _data->tileOffsets.readFrom(part->chunkOffsets,_data->fileIsComplete);
882 _data->_streamData->currentPosition = _data->_streamData->is->tellg();
883 }
884
885
886 void
initialize()887 TiledInputFile::initialize ()
888 {
889 // fix bad types in header (arises when a tool built against an older version of
890 // OpenEXR converts a scanline image to tiled)
891 // only applies when file is a single part, regular image, tiled file
892 //
893 if(!isMultiPart(_data->version) &&
894 !isNonImage(_data->version) &&
895 isTiled(_data->version) &&
896 _data->header.hasType() )
897 {
898 _data->header.setType(TILEDIMAGE);
899 }
900
901 if (_data->partNumber == -1)
902 {
903 if (!isTiled (_data->version))
904 throw IEX_NAMESPACE::ArgExc ("Expected a tiled file but the file is not tiled.");
905
906 }
907 else
908 {
909 if(_data->header.hasType() && _data->header.type()!=TILEDIMAGE)
910 {
911 throw IEX_NAMESPACE::ArgExc ("TiledInputFile used for non-tiledimage part.");
912 }
913 }
914
915 _data->header.sanityCheck (true);
916
917 _data->tileDesc = _data->header.tileDescription();
918 _data->lineOrder = _data->header.lineOrder();
919
920 //
921 // Save the dataWindow information
922 //
923
924 const Box2i &dataWindow = _data->header.dataWindow();
925 _data->minX = dataWindow.min.x;
926 _data->maxX = dataWindow.max.x;
927 _data->minY = dataWindow.min.y;
928 _data->maxY = dataWindow.max.y;
929
930 //
931 // Precompute level and tile information to speed up utility functions
932 //
933
934 precalculateTileInfo (_data->tileDesc,
935 _data->minX, _data->maxX,
936 _data->minY, _data->maxY,
937 _data->numXTiles, _data->numYTiles,
938 _data->numXLevels, _data->numYLevels);
939
940 _data->bytesPerPixel = calculateBytesPerPixel (_data->header);
941
942 _data->maxBytesPerTileLine = _data->bytesPerPixel * _data->tileDesc.xSize;
943
944 _data->tileBufferSize = _data->maxBytesPerTileLine * _data->tileDesc.ySize;
945
946 //
947 // Create all the TileBuffers and allocate their internal buffers
948 //
949
950 for (size_t i = 0; i < _data->tileBuffers.size(); i++)
951 {
952 _data->tileBuffers[i] = new TileBuffer (newTileCompressor
953 (_data->header.compression(),
954 _data->maxBytesPerTileLine,
955 _data->tileDesc.ySize,
956 _data->header));
957
958 if (!_data->_streamData->is->isMemoryMapped ())
959 _data->tileBuffers[i]->buffer = new char [_data->tileBufferSize];
960 }
961
962 _data->tileOffsets = TileOffsets (_data->tileDesc.mode,
963 _data->numXLevels,
964 _data->numYLevels,
965 _data->numXTiles,
966 _data->numYTiles);
967 }
968
969
~TiledInputFile()970 TiledInputFile::~TiledInputFile ()
971 {
972 if (!_data->memoryMapped)
973 for (size_t i = 0; i < _data->tileBuffers.size(); i++)
974 delete [] _data->tileBuffers[i]->buffer;
975
976 if (_data->_deleteStream)
977 delete _data->_streamData->is;
978
979 if (_data->partNumber == -1)
980 delete _data->_streamData;
981
982 delete _data;
983 }
984
985
986 const char *
fileName() const987 TiledInputFile::fileName () const
988 {
989 return _data->_streamData->is->fileName();
990 }
991
992
993 const Header &
header() const994 TiledInputFile::header () const
995 {
996 return _data->header;
997 }
998
999
1000 int
version() const1001 TiledInputFile::version () const
1002 {
1003 return _data->version;
1004 }
1005
1006
1007 void
setFrameBuffer(const FrameBuffer & frameBuffer)1008 TiledInputFile::setFrameBuffer (const FrameBuffer &frameBuffer)
1009 {
1010 Lock lock (*_data->_streamData);
1011
1012 //
1013 // Set the frame buffer
1014 //
1015
1016 //
1017 // Check if the new frame buffer descriptor is
1018 // compatible with the image file header.
1019 //
1020
1021 const ChannelList &channels = _data->header.channels();
1022
1023 for (FrameBuffer::ConstIterator j = frameBuffer.begin();
1024 j != frameBuffer.end();
1025 ++j)
1026 {
1027 ChannelList::ConstIterator i = channels.find (j.name());
1028
1029 if (i == channels.end())
1030 continue;
1031
1032 if (i.channel().xSampling != j.slice().xSampling ||
1033 i.channel().ySampling != j.slice().ySampling)
1034 THROW (IEX_NAMESPACE::ArgExc, "X and/or y subsampling factors "
1035 "of \"" << i.name() << "\" channel "
1036 "of input file \"" << fileName() << "\" are "
1037 "not compatible with the frame buffer's "
1038 "subsampling factors.");
1039 }
1040
1041 //
1042 // Initialize the slice table for readPixels().
1043 //
1044
1045 vector<TInSliceInfo> slices;
1046 ChannelList::ConstIterator i = channels.begin();
1047
1048 for (FrameBuffer::ConstIterator j = frameBuffer.begin();
1049 j != frameBuffer.end();
1050 ++j)
1051 {
1052 while (i != channels.end() && strcmp (i.name(), j.name()) < 0)
1053 {
1054 //
1055 // Channel i is present in the file but not
1056 // in the frame buffer; data for channel i
1057 // will be skipped during readPixels().
1058 //
1059
1060 slices.push_back (TInSliceInfo (i.channel().type,
1061 i.channel().type,
1062 0, // base
1063 0, // xStride
1064 0, // yStride
1065 false, // fill
1066 true, // skip
1067 0.0)); // fillValue
1068 ++i;
1069 }
1070
1071 bool fill = false;
1072
1073 if (i == channels.end() || strcmp (i.name(), j.name()) > 0)
1074 {
1075 //
1076 // Channel i is present in the frame buffer, but not in the file.
1077 // In the frame buffer, slice j will be filled with a default value.
1078 //
1079
1080 fill = true;
1081 }
1082
1083 slices.push_back (TInSliceInfo (j.slice().type,
1084 fill? j.slice().type: i.channel().type,
1085 j.slice().base,
1086 j.slice().xStride,
1087 j.slice().yStride,
1088 fill,
1089 false, // skip
1090 j.slice().fillValue,
1091 (j.slice().xTileCoords)? 1: 0,
1092 (j.slice().yTileCoords)? 1: 0));
1093
1094 if (i != channels.end() && !fill)
1095 ++i;
1096 }
1097
1098 while (i != channels.end())
1099 {
1100 //
1101 // Channel i is present in the file but not
1102 // in the frame buffer; data for channel i
1103 // will be skipped during readPixels().
1104 //
1105
1106 slices.push_back (TInSliceInfo (i.channel().type,
1107 i.channel().type,
1108 0, // base
1109 0, // xStride
1110 0, // yStride
1111 false, // fill
1112 true, // skip
1113 0.0)); // fillValue
1114 ++i;
1115 }
1116
1117 //
1118 // Store the new frame buffer.
1119 //
1120
1121 _data->frameBuffer = frameBuffer;
1122 _data->slices = slices;
1123 }
1124
1125
1126 const FrameBuffer &
frameBuffer() const1127 TiledInputFile::frameBuffer () const
1128 {
1129 Lock lock (*_data->_streamData);
1130 return _data->frameBuffer;
1131 }
1132
1133
1134 bool
isComplete() const1135 TiledInputFile::isComplete () const
1136 {
1137 return _data->fileIsComplete;
1138 }
1139
1140
1141 void
readTiles(int dx1,int dx2,int dy1,int dy2,int lx,int ly)1142 TiledInputFile::readTiles (int dx1, int dx2, int dy1, int dy2, int lx, int ly)
1143 {
1144 //
1145 // Read a range of tiles from the file into the framebuffer
1146 //
1147
1148 try
1149 {
1150 Lock lock (*_data->_streamData);
1151
1152 if (_data->slices.size() == 0)
1153 throw IEX_NAMESPACE::ArgExc ("No frame buffer specified "
1154 "as pixel data destination.");
1155
1156 if (!isValidLevel (lx, ly))
1157 THROW (IEX_NAMESPACE::ArgExc,
1158 "Level coordinate "
1159 "(" << lx << ", " << ly << ") "
1160 "is invalid.");
1161
1162 //
1163 // Determine the first and last tile coordinates in both dimensions.
1164 // We always attempt to read the range of tiles in the order that
1165 // they are stored in the file.
1166 //
1167
1168 if (dx1 > dx2)
1169 std::swap (dx1, dx2);
1170
1171 if (dy1 > dy2)
1172 std::swap (dy1, dy2);
1173
1174 int dyStart = dy1;
1175 int dyStop = dy2 + 1;
1176 int dY = 1;
1177
1178 if (_data->lineOrder == DECREASING_Y)
1179 {
1180 dyStart = dy2;
1181 dyStop = dy1 - 1;
1182 dY = -1;
1183 }
1184
1185 //
1186 // Create a task group for all tile buffer tasks. When the
1187 // task group goes out of scope, the destructor waits until
1188 // all tasks are complete.
1189 //
1190
1191 {
1192 TaskGroup taskGroup;
1193 int tileNumber = 0;
1194
1195 for (int dy = dyStart; dy != dyStop; dy += dY)
1196 {
1197 for (int dx = dx1; dx <= dx2; dx++)
1198 {
1199 if (!isValidTile (dx, dy, lx, ly))
1200 THROW (IEX_NAMESPACE::ArgExc,
1201 "Tile (" << dx << ", " << dy << ", " <<
1202 lx << "," << ly << ") is not a valid tile.");
1203
1204 ThreadPool::addGlobalTask (newTileBufferTask (&taskGroup,
1205 _data->_streamData,
1206 _data,
1207 tileNumber++,
1208 dx, dy,
1209 lx, ly));
1210 }
1211 }
1212
1213 //
1214 // finish all tasks
1215 //
1216 }
1217
1218 //
1219 // Exeption handling:
1220 //
1221 // TileBufferTask::execute() may have encountered exceptions, but
1222 // those exceptions occurred in another thread, not in the thread
1223 // that is executing this call to TiledInputFile::readTiles().
1224 // TileBufferTask::execute() has caught all exceptions and stored
1225 // the exceptions' what() strings in the tile buffers.
1226 // Now we check if any tile buffer contains a stored exception; if
1227 // this is the case then we re-throw the exception in this thread.
1228 // (It is possible that multiple tile buffers contain stored
1229 // exceptions. We re-throw the first exception we find and
1230 // ignore all others.)
1231 //
1232
1233 const string *exception = 0;
1234
1235 for (size_t i = 0; i < _data->tileBuffers.size(); ++i)
1236 {
1237 TileBuffer *tileBuffer = _data->tileBuffers[i];
1238
1239 if (tileBuffer->hasException && !exception)
1240 exception = &tileBuffer->exception;
1241
1242 tileBuffer->hasException = false;
1243 }
1244
1245 if (exception)
1246 throw IEX_NAMESPACE::IoExc (*exception);
1247 }
1248 catch (IEX_NAMESPACE::BaseExc &e)
1249 {
1250 REPLACE_EXC (e, "Error reading pixel data from image "
1251 "file \"" << fileName() << "\". " << e);
1252 throw;
1253 }
1254 }
1255
1256
1257 void
readTiles(int dx1,int dx2,int dy1,int dy2,int l)1258 TiledInputFile::readTiles (int dx1, int dx2, int dy1, int dy2, int l)
1259 {
1260 readTiles (dx1, dx2, dy1, dy2, l, l);
1261 }
1262
1263
1264 void
readTile(int dx,int dy,int lx,int ly)1265 TiledInputFile::readTile (int dx, int dy, int lx, int ly)
1266 {
1267 readTiles (dx, dx, dy, dy, lx, ly);
1268 }
1269
1270
1271 void
readTile(int dx,int dy,int l)1272 TiledInputFile::readTile (int dx, int dy, int l)
1273 {
1274 readTile (dx, dy, l, l);
1275 }
1276
1277
1278 void
rawTileData(int & dx,int & dy,int & lx,int & ly,const char * & pixelData,int & pixelDataSize)1279 TiledInputFile::rawTileData (int &dx, int &dy,
1280 int &lx, int &ly,
1281 const char *&pixelData,
1282 int &pixelDataSize)
1283 {
1284 try
1285 {
1286 Lock lock (*_data->_streamData);
1287
1288 if (!isValidTile (dx, dy, lx, ly))
1289 throw IEX_NAMESPACE::ArgExc ("Tried to read a tile outside "
1290 "the image file's data window.");
1291
1292 TileBuffer *tileBuffer = _data->getTileBuffer (0);
1293
1294 //
1295 // if file is a multipart file, we have to seek to the required tile
1296 // since we don't know where the file pointer is
1297 //
1298 int old_dx=dx;
1299 int old_dy=dy;
1300 int old_lx=lx;
1301 int old_ly=ly;
1302 if(isMultiPart(version()))
1303 {
1304 _data->_streamData->is->seekg(_data->tileOffsets(dx,dy,lx,ly));
1305 }
1306 readNextTileData (_data->_streamData, _data, dx, dy, lx, ly,
1307 tileBuffer->buffer,
1308 pixelDataSize);
1309 if(isMultiPart(version()))
1310 {
1311 if (old_dx!=dx || old_dy !=dy || old_lx!=lx || old_ly!=ly)
1312 {
1313 throw IEX_NAMESPACE::ArgExc ("rawTileData read the wrong tile");
1314 }
1315 }
1316 pixelData = tileBuffer->buffer;
1317 }
1318 catch (IEX_NAMESPACE::BaseExc &e)
1319 {
1320 REPLACE_EXC (e, "Error reading pixel data from image "
1321 "file \"" << fileName() << "\". " << e);
1322 throw;
1323 }
1324 }
1325
1326
1327 unsigned int
tileXSize() const1328 TiledInputFile::tileXSize () const
1329 {
1330 return _data->tileDesc.xSize;
1331 }
1332
1333
1334 unsigned int
tileYSize() const1335 TiledInputFile::tileYSize () const
1336 {
1337 return _data->tileDesc.ySize;
1338 }
1339
1340
1341 LevelMode
levelMode() const1342 TiledInputFile::levelMode () const
1343 {
1344 return _data->tileDesc.mode;
1345 }
1346
1347
1348 LevelRoundingMode
levelRoundingMode() const1349 TiledInputFile::levelRoundingMode () const
1350 {
1351 return _data->tileDesc.roundingMode;
1352 }
1353
1354
1355 int
numLevels() const1356 TiledInputFile::numLevels () const
1357 {
1358 if (levelMode() == RIPMAP_LEVELS)
1359 THROW (IEX_NAMESPACE::LogicExc, "Error calling numLevels() on image "
1360 "file \"" << fileName() << "\" "
1361 "(numLevels() is not defined for files "
1362 "with RIPMAP level mode).");
1363
1364 return _data->numXLevels;
1365 }
1366
1367
1368 int
numXLevels() const1369 TiledInputFile::numXLevels () const
1370 {
1371 return _data->numXLevels;
1372 }
1373
1374
1375 int
numYLevels() const1376 TiledInputFile::numYLevels () const
1377 {
1378 return _data->numYLevels;
1379 }
1380
1381
1382 bool
isValidLevel(int lx,int ly) const1383 TiledInputFile::isValidLevel (int lx, int ly) const
1384 {
1385 if (lx < 0 || ly < 0)
1386 return false;
1387
1388 if (levelMode() == MIPMAP_LEVELS && lx != ly)
1389 return false;
1390
1391 if (lx >= numXLevels() || ly >= numYLevels())
1392 return false;
1393
1394 return true;
1395 }
1396
1397
1398 int
levelWidth(int lx) const1399 TiledInputFile::levelWidth (int lx) const
1400 {
1401 try
1402 {
1403 return levelSize (_data->minX, _data->maxX, lx,
1404 _data->tileDesc.roundingMode);
1405 }
1406 catch (IEX_NAMESPACE::BaseExc &e)
1407 {
1408 REPLACE_EXC (e, "Error calling levelWidth() on image "
1409 "file \"" << fileName() << "\". " << e);
1410 throw;
1411 }
1412 }
1413
1414
1415 int
levelHeight(int ly) const1416 TiledInputFile::levelHeight (int ly) const
1417 {
1418 try
1419 {
1420 return levelSize (_data->minY, _data->maxY, ly,
1421 _data->tileDesc.roundingMode);
1422 }
1423 catch (IEX_NAMESPACE::BaseExc &e)
1424 {
1425 REPLACE_EXC (e, "Error calling levelHeight() on image "
1426 "file \"" << fileName() << "\". " << e);
1427 throw;
1428 }
1429 }
1430
1431
1432 int
numXTiles(int lx) const1433 TiledInputFile::numXTiles (int lx) const
1434 {
1435 if (lx < 0 || lx >= _data->numXLevels)
1436 {
1437 THROW (IEX_NAMESPACE::ArgExc, "Error calling numXTiles() on image "
1438 "file \"" << _data->_streamData->is->fileName() << "\" "
1439 "(Argument is not in valid range).");
1440
1441 }
1442
1443 return _data->numXTiles[lx];
1444 }
1445
1446
1447 int
numYTiles(int ly) const1448 TiledInputFile::numYTiles (int ly) const
1449 {
1450 if (ly < 0 || ly >= _data->numYLevels)
1451 {
1452 THROW (IEX_NAMESPACE::ArgExc, "Error calling numYTiles() on image "
1453 "file \"" << _data->_streamData->is->fileName() << "\" "
1454 "(Argument is not in valid range).");
1455 }
1456
1457 return _data->numYTiles[ly];
1458 }
1459
1460
1461 Box2i
dataWindowForLevel(int l) const1462 TiledInputFile::dataWindowForLevel (int l) const
1463 {
1464 return dataWindowForLevel (l, l);
1465 }
1466
1467
1468 Box2i
dataWindowForLevel(int lx,int ly) const1469 TiledInputFile::dataWindowForLevel (int lx, int ly) const
1470 {
1471 try
1472 {
1473 return OPENEXR_IMF_INTERNAL_NAMESPACE::dataWindowForLevel (
1474 _data->tileDesc,
1475 _data->minX, _data->maxX,
1476 _data->minY, _data->maxY,
1477 lx, ly);
1478 }
1479 catch (IEX_NAMESPACE::BaseExc &e)
1480 {
1481 REPLACE_EXC (e, "Error calling dataWindowForLevel() on image "
1482 "file \"" << fileName() << "\". " << e);
1483 throw;
1484 }
1485 }
1486
1487
1488 Box2i
dataWindowForTile(int dx,int dy,int l) const1489 TiledInputFile::dataWindowForTile (int dx, int dy, int l) const
1490 {
1491 return dataWindowForTile (dx, dy, l, l);
1492 }
1493
1494
1495 Box2i
dataWindowForTile(int dx,int dy,int lx,int ly) const1496 TiledInputFile::dataWindowForTile (int dx, int dy, int lx, int ly) const
1497 {
1498 try
1499 {
1500 if (!isValidTile (dx, dy, lx, ly))
1501 throw IEX_NAMESPACE::ArgExc ("Arguments not in valid range.");
1502
1503 return OPENEXR_IMF_INTERNAL_NAMESPACE::dataWindowForTile (
1504 _data->tileDesc,
1505 _data->minX, _data->maxX,
1506 _data->minY, _data->maxY,
1507 dx, dy, lx, ly);
1508 }
1509 catch (IEX_NAMESPACE::BaseExc &e)
1510 {
1511 REPLACE_EXC (e, "Error calling dataWindowForTile() on image "
1512 "file \"" << fileName() << "\". " << e);
1513 throw;
1514 }
1515 }
1516
1517
1518 bool
isValidTile(int dx,int dy,int lx,int ly) const1519 TiledInputFile::isValidTile (int dx, int dy, int lx, int ly) const
1520 {
1521 return ((lx < _data->numXLevels && lx >= 0) &&
1522 (ly < _data->numYLevels && ly >= 0) &&
1523 (dx < _data->numXTiles[lx] && dx >= 0) &&
1524 (dy < _data->numYTiles[ly] && dy >= 0));
1525 }
1526
tileOrder(int dx[],int dy[],int lx[],int ly[]) const1527 void TiledInputFile::tileOrder(int dx[], int dy[], int lx[], int ly[]) const
1528 {
1529 return _data->tileOffsets.getTileOrder(dx,dy,lx,ly);
1530 }
1531
1532
1533 OPENEXR_IMF_INTERNAL_NAMESPACE_SOURCE_EXIT
1534