1 ///////////////////////////////////////////////////////////////////////////
2 //
3 // Copyright (c) 2011, 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 DeepTiledInputFile
38 //
39 //-----------------------------------------------------------------------------
40
41 #include <ImfDeepTiledInputFile.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 "ImathBox.h"
49 #include <ImfXdr.h>
50 #include <ImfConvert.h>
51 #include <ImfVersion.h>
52 #include <ImfTileOffsets.h>
53 #include <ImfThreading.h>
54 #include <ImfPartType.h>
55 #include <ImfMultiPartInputFile.h>
56 #include "IlmThreadPool.h"
57 #include "IlmThreadSemaphore.h"
58 #include "IlmThreadMutex.h"
59 #include "ImfInputStreamMutex.h"
60 #include "ImfInputPartData.h"
61 #include "ImathVec.h"
62 #include "Iex.h"
63 #include <string>
64 #include <vector>
65 #include <algorithm>
66 #include <assert.h>
67 #include <limits>
68
69 #include "ImfNamespace.h"
70
71 OPENEXR_IMF_INTERNAL_NAMESPACE_SOURCE_ENTER
72
73 using IMATH_NAMESPACE::Box2i;
74 using IMATH_NAMESPACE::V2i;
75 using std::string;
76 using std::vector;
77 using std::min;
78 using std::max;
79 using ILMTHREAD_NAMESPACE::Mutex;
80 using ILMTHREAD_NAMESPACE::Lock;
81 using ILMTHREAD_NAMESPACE::Semaphore;
82 using ILMTHREAD_NAMESPACE::Task;
83 using ILMTHREAD_NAMESPACE::TaskGroup;
84 using ILMTHREAD_NAMESPACE::ThreadPool;
85
86 namespace {
87
88 struct TInSliceInfo
89 {
90 PixelType typeInFrameBuffer;
91 PixelType typeInFile;
92 char* pointerArrayBase;
93 size_t xStride;
94 size_t yStride;
95 ptrdiff_t sampleStride;
96 bool fill;
97 bool skip;
98 double fillValue;
99 int xTileCoords;
100 int yTileCoords;
101
102 TInSliceInfo (PixelType typeInFrameBuffer = HALF,
103 char * base = NULL,
104 PixelType typeInFile = HALF,
105 size_t xStride = 0,
106 size_t yStride = 0,
107 ptrdiff_t sampleStride = 0,
108 bool fill = false,
109 bool skip = false,
110 double fillValue = 0.0,
111 int xTileCoords = 0,
112 int yTileCoords = 0);
113 };
114
115
TInSliceInfo(PixelType tifb,char * b,PixelType tifl,size_t xs,size_t ys,ptrdiff_t spst,bool f,bool s,double fv,int xtc,int ytc)116 TInSliceInfo::TInSliceInfo (PixelType tifb,
117 char * b,
118 PixelType tifl,
119 size_t xs, size_t ys,
120 ptrdiff_t spst,
121 bool f, bool s,
122 double fv,
123 int xtc,
124 int ytc)
125 :
126 typeInFrameBuffer (tifb),
127 typeInFile (tifl),
128 pointerArrayBase (b),
129 xStride (xs),
130 yStride (ys),
131 sampleStride (spst),
132 fill (f),
133 skip (s),
134 fillValue (fv),
135 xTileCoords (xtc),
136 yTileCoords (ytc)
137 {
138 // empty
139 }
140
141
142 struct TileBuffer
143 {
144 Array2D<unsigned int> sampleCount;
145 const char * uncompressedData;
146 char * buffer;
147 Int64 dataSize;
148 Int64 uncompressedDataSize;
149 Compressor * compressor;
150 Compressor::Format format;
151 int dx;
152 int dy;
153 int lx;
154 int ly;
155 bool hasException;
156 string exception;
157
158 TileBuffer ();
159 ~TileBuffer ();
160
wait__anon5e3d8a3c0111::TileBuffer161 inline void wait () {_sem.wait();}
post__anon5e3d8a3c0111::TileBuffer162 inline void post () {_sem.post();}
163
164 protected:
165
166 Semaphore _sem;
167 };
168
169
TileBuffer()170 TileBuffer::TileBuffer ():
171 uncompressedData (0),
172 buffer (0),
173 dataSize (0),
174 compressor (0),
175 format (defaultFormat (compressor)),
176 dx (-1),
177 dy (-1),
178 lx (-1),
179 ly (-1),
180 hasException (false),
181 exception (),
182 _sem (1)
183 {
184 // empty
185 }
186
187
~TileBuffer()188 TileBuffer::~TileBuffer ()
189 {
190 delete compressor;
191 }
192
193 } // namespace
194
195
196 class MultiPartInputFile;
197
198
199 //
200 // struct TiledInputFile::Data stores things that will be
201 // needed between calls to readTile()
202 //
203
204 struct DeepTiledInputFile::Data: public Mutex
205 {
206 Header header; // the image header
207 TileDescription tileDesc; // describes the tile layout
208 int version; // file's version
209 DeepFrameBuffer frameBuffer; // framebuffer to write into
210 LineOrder lineOrder; // the file's lineorder
211 int minX; // data window's min x coord
212 int maxX; // data window's max x coord
213 int minY; // data window's min y coord
214 int maxY; // data window's max x coord
215
216 int numXLevels; // number of x levels
217 int numYLevels; // number of y levels
218 int * numXTiles; // number of x tiles at a level
219 int * numYTiles; // number of y tiles at a level
220
221 TileOffsets tileOffsets; // stores offsets in file for
222 // each tile
223
224 bool fileIsComplete; // True if no tiles are missing
225 // in the file
226
227 vector<TInSliceInfo*> slices; // info about channels in file
228
229 // ourselves? or does someone
230 // else do it?
231
232 int partNumber; // part number
233
234 bool multiPartBackwardSupport; // if we are reading a multipart file
235 // using OpenEXR 1.7 API
236
237 int numThreads; // number of threads
238
239 MultiPartInputFile* multiPartFile; // the MultiPartInputFile used to
240 // support backward compatibility
241
242 vector<TileBuffer*> tileBuffers; // each holds a single tile
243
244 bool memoryMapped; // if the stream is memory mapped
245
246 char* sampleCountSliceBase; // pointer to the start of
247 // the sample count array
248 ptrdiff_t sampleCountXStride; // x stride of the sample count array
249 ptrdiff_t sampleCountYStride; // y stride of the sample count array
250
251 int sampleCountXTileCoords; // the value of xTileCoords from the
252 // sample count slice
253 int sampleCountYTileCoords; // the value of yTileCoords from the
254 // sample count slice
255
256 Array<char> sampleCountTableBuffer; // the buffer for sample count table
257
258 Compressor* sampleCountTableComp; // the decompressor for sample count table
259
260 Int64 maxSampleCountTableSize; // the max size in bytes for a pixel
261 // sample count table
262 int combinedSampleSize; // total size of all channels combined to check sampletable size
263
264 InputStreamMutex * _streamData;
265 bool _deleteStream; // should we delete the stream
266 Data (int numThreads);
267 ~Data ();
268
269 inline TileBuffer * getTileBuffer (int number);
270 // hash function from tile indices
271 // into our vector of tile buffers
272
273 int& getSampleCount(int x, int y);
274 // get the number of samples
275 // in each pixel
276 };
277
278
Data(int numThreads)279 DeepTiledInputFile::Data::Data (int numThreads):
280 numXTiles (0),
281 numYTiles (0),
282 partNumber (-1),
283 multiPartBackwardSupport(false),
284 numThreads(numThreads),
285 memoryMapped(false),
286 _streamData(NULL),
287 _deleteStream(false)
288 {
289 //
290 // We need at least one tileBuffer, but if threading is used,
291 // to keep n threads busy we need 2*n tileBuffers
292 //
293
294 tileBuffers.resize (max (1, 2 * numThreads));
295 }
296
297
~Data()298 DeepTiledInputFile::Data::~Data ()
299 {
300 delete [] numXTiles;
301 delete [] numYTiles;
302
303 for (size_t i = 0; i < tileBuffers.size(); i++)
304 delete tileBuffers[i];
305
306 if (multiPartBackwardSupport)
307 delete multiPartFile;
308
309 for (size_t i = 0; i < slices.size(); i++)
310 delete slices[i];
311 }
312
313
314 TileBuffer*
getTileBuffer(int number)315 DeepTiledInputFile::Data::getTileBuffer (int number)
316 {
317 return tileBuffers[number % tileBuffers.size()];
318 }
319
320
321 int&
getSampleCount(int x,int y)322 DeepTiledInputFile::Data::getSampleCount(int x, int y)
323 {
324 return sampleCount(sampleCountSliceBase,
325 sampleCountXStride,
326 sampleCountYStride,
327 x, y);
328 }
329
330
331 namespace {
332
333 void
readTileData(InputStreamMutex * streamData,DeepTiledInputFile::Data * ifd,int dx,int dy,int lx,int ly,char * & buffer,Int64 & dataSize,Int64 & unpackedDataSize)334 readTileData (InputStreamMutex *streamData,
335 DeepTiledInputFile::Data *ifd,
336 int dx, int dy,
337 int lx, int ly,
338 char *&buffer,
339 Int64 &dataSize,
340 Int64 &unpackedDataSize)
341 {
342 //
343 // Read a single tile block from the file and into the array pointed
344 // to by buffer. If the file is memory-mapped, then we change where
345 // buffer points instead of writing into the array (hence buffer needs
346 // to be a reference to a char *).
347 //
348
349 //
350 // Look up the location for this tile in the Index and
351 // seek to that position if necessary
352 //
353
354 Int64 tileOffset = ifd->tileOffsets (dx, dy, lx, ly);
355
356 if (tileOffset == 0)
357 {
358 THROW (IEX_NAMESPACE::InputExc, "Tile (" << dx << ", " << dy << ", " <<
359 lx << ", " << ly << ") is missing.");
360 }
361
362 //
363 // In a multi-part file, the next chunk does not need to
364 // belong to the same part, so we have to compare the
365 // offset here.
366 //
367
368 if ( !isMultiPart(ifd->version) )
369 {
370 if (streamData->currentPosition != tileOffset)
371 streamData->is->seekg(tileOffset);
372 }
373 else
374 {
375 //
376 // In a multi-part file, the file pointer may be moved by other
377 // parts, so we have to ask tellg() where we are.
378 //
379 if (streamData->is->tellg() != tileOffset)
380 streamData->is->seekg (tileOffset);
381 }
382
383 //
384 // Read the first few bytes of the tile (the header).
385 // Verify that the tile coordinates and the level number
386 // are correct.
387 //
388
389 int tileXCoord, tileYCoord, levelX, levelY;
390
391 if (isMultiPart(ifd->version))
392 {
393 int partNumber;
394 Xdr::read <StreamIO> (*streamData->is, partNumber);
395 if (partNumber != ifd->partNumber)
396 {
397 THROW (IEX_NAMESPACE::ArgExc, "Unexpected part number " << partNumber
398 << ", should be " << ifd->partNumber << ".");
399 }
400 }
401
402 Xdr::read <StreamIO> (*streamData->is, tileXCoord);
403 Xdr::read <StreamIO> (*streamData->is, tileYCoord);
404 Xdr::read <StreamIO> (*streamData->is, levelX);
405 Xdr::read <StreamIO> (*streamData->is, levelY);
406
407 Int64 tableSize;
408 Xdr::read <StreamIO> (*streamData->is, tableSize);
409
410 Xdr::read <StreamIO> (*streamData->is, dataSize);
411 Xdr::read <StreamIO> (*streamData->is, unpackedDataSize);
412
413
414 //
415 // Skip the pixel sample count table because we have read this data.
416 //
417
418 Xdr::skip <StreamIO> (*streamData->is, tableSize);
419
420
421 if (tileXCoord != dx)
422 throw IEX_NAMESPACE::InputExc ("Unexpected tile x coordinate.");
423
424 if (tileYCoord != dy)
425 throw IEX_NAMESPACE::InputExc ("Unexpected tile y coordinate.");
426
427 if (levelX != lx)
428 throw IEX_NAMESPACE::InputExc ("Unexpected tile x level number coordinate.");
429
430 if (levelY != ly)
431 throw IEX_NAMESPACE::InputExc ("Unexpected tile y level number coordinate.");
432
433 //
434 // Read the pixel data.
435 //
436
437 if (streamData->is->isMemoryMapped ())
438 buffer = streamData->is->readMemoryMapped (dataSize);
439 else
440 {
441 // (TODO) check if the packed data size is too big?
442 // (TODO) better memory management here. Don't delete buffer everytime.
443 if (buffer != 0) delete[] buffer;
444 buffer = new char[dataSize];
445 streamData->is->read (buffer, dataSize);
446 }
447
448 //
449 // Keep track of which tile is the next one in
450 // the file, so that we can avoid redundant seekg()
451 // operations (seekg() can be fairly expensive).
452 //
453
454 streamData->currentPosition = tileOffset + 4 * Xdr::size<int>() +
455 3 * Xdr::size<Int64>() +
456 tableSize +
457 dataSize;
458 }
459
460
461 void
readNextTileData(InputStreamMutex * streamData,DeepTiledInputFile::Data * ifd,int & dx,int & dy,int & lx,int & ly,char * & buffer,Int64 & dataSize,Int64 & unpackedDataSize)462 readNextTileData (InputStreamMutex *streamData,
463 DeepTiledInputFile::Data *ifd,
464 int &dx, int &dy,
465 int &lx, int &ly,
466 char * & buffer,
467 Int64 &dataSize,
468 Int64 &unpackedDataSize)
469 {
470 //
471 // Read the next tile block from the file
472 //
473
474 //
475 // Read the first few bytes of the tile (the header).
476 //
477
478 Xdr::read <StreamIO> (*streamData->is, dx);
479 Xdr::read <StreamIO> (*streamData->is, dy);
480 Xdr::read <StreamIO> (*streamData->is, lx);
481 Xdr::read <StreamIO> (*streamData->is, ly);
482
483 Int64 tableSize;
484 Xdr::read <StreamIO> (*streamData->is, tableSize);
485
486 Xdr::read <StreamIO> (*streamData->is, dataSize);
487 Xdr::read <StreamIO> (*streamData->is, unpackedDataSize);
488
489 //
490 // Skip the pixel sample count table because we have read this data.
491 //
492
493 Xdr::skip <StreamIO> (*streamData->is, tableSize);
494
495 //
496 // Read the pixel data.
497 //
498
499 streamData->is->read (buffer, dataSize);
500
501 //
502 // Keep track of which tile is the next one in
503 // the file, so that we can avoid redundant seekg()
504 // operations (seekg() can be fairly expensive).
505 //
506
507 streamData->currentPosition += 4 * Xdr::size<int>() +
508 3 * Xdr::size<Int64>() +
509 tableSize +
510 dataSize;
511 }
512
513
514 //
515 // A TileBufferTask encapsulates the task of uncompressing
516 // a single tile and copying it into the frame buffer.
517 //
518
519 class TileBufferTask : public Task
520 {
521 public:
522
523 TileBufferTask (TaskGroup *group,
524 DeepTiledInputFile::Data *ifd,
525 TileBuffer *tileBuffer);
526
527 virtual ~TileBufferTask ();
528
529 virtual void execute ();
530
531 private:
532
533 DeepTiledInputFile::Data * _ifd;
534 TileBuffer * _tileBuffer;
535 };
536
537
TileBufferTask(TaskGroup * group,DeepTiledInputFile::Data * ifd,TileBuffer * tileBuffer)538 TileBufferTask::TileBufferTask
539 (TaskGroup *group,
540 DeepTiledInputFile::Data *ifd,
541 TileBuffer *tileBuffer)
542 :
543 Task (group),
544 _ifd (ifd),
545 _tileBuffer (tileBuffer)
546 {
547 // empty
548 }
549
550
~TileBufferTask()551 TileBufferTask::~TileBufferTask ()
552 {
553 //
554 // Signal that the tile buffer is now free
555 //
556
557 _tileBuffer->post ();
558 }
559
560
561 void
execute()562 TileBufferTask::execute ()
563 {
564 try
565 {
566 //
567 // Calculate information about the tile
568 //
569
570 Box2i tileRange = OPENEXR_IMF_INTERNAL_NAMESPACE::dataWindowForTile (
571 _ifd->tileDesc,
572 _ifd->minX, _ifd->maxX,
573 _ifd->minY, _ifd->maxY,
574 _tileBuffer->dx,
575 _tileBuffer->dy,
576 _tileBuffer->lx,
577 _tileBuffer->ly);
578
579 //
580 // Get the size of the tile.
581 //
582
583 Array<unsigned int> numPixelsPerScanLine;
584 numPixelsPerScanLine.resizeErase(tileRange.max.y - tileRange.min.y + 1);
585
586 int sizeOfTile = 0;
587 int maxBytesPerTileLine = 0;
588
589 for (int y = tileRange.min.y; y <= tileRange.max.y; y++)
590 {
591 numPixelsPerScanLine[y - tileRange.min.y] = 0;
592
593 int bytesPerLine = 0;
594
595 for (int x = tileRange.min.x; x <= tileRange.max.x; x++)
596 {
597 int xOffset = _ifd->sampleCountXTileCoords * tileRange.min.x;
598 int yOffset = _ifd->sampleCountYTileCoords * tileRange.min.y;
599
600 int count = _ifd->getSampleCount(x - xOffset, y - yOffset);
601 for (unsigned int c = 0; c < _ifd->slices.size(); ++c)
602 {
603 sizeOfTile += count * pixelTypeSize(_ifd->slices[c]->typeInFile);
604 bytesPerLine += count * pixelTypeSize(_ifd->slices[c]->typeInFile);
605 }
606 numPixelsPerScanLine[y - tileRange.min.y] += count;
607 }
608
609 if (bytesPerLine > maxBytesPerTileLine)
610 maxBytesPerTileLine = bytesPerLine;
611 }
612
613 // (TODO) don't do this every time.
614 if (_tileBuffer->compressor != 0)
615 delete _tileBuffer->compressor;
616 _tileBuffer->compressor = newTileCompressor
617 (_ifd->header.compression(),
618 maxBytesPerTileLine,
619 _ifd->tileDesc.ySize,
620 _ifd->header);
621
622 //
623 // Uncompress the data, if necessary
624 //
625
626 if (_tileBuffer->compressor && _tileBuffer->dataSize < Int64(sizeOfTile))
627 {
628 _tileBuffer->format = _tileBuffer->compressor->format();
629
630 _tileBuffer->dataSize = _tileBuffer->compressor->uncompressTile
631 (_tileBuffer->buffer, _tileBuffer->dataSize,
632 tileRange, _tileBuffer->uncompressedData);
633 }
634 else
635 {
636 //
637 // If the line is uncompressed, it's in XDR format,
638 // regardless of the compressor's output format.
639 //
640
641 _tileBuffer->format = Compressor::XDR;
642 _tileBuffer->uncompressedData = _tileBuffer->buffer;
643 }
644
645 //
646 // Convert the tile of pixel data back from the machine-independent
647 // representation, and store the result in the frame buffer.
648 //
649
650 const char *readPtr = _tileBuffer->uncompressedData;
651 // points to where we
652 // read from in the
653 // tile block
654
655 //
656 // Iterate over the scan lines in the tile.
657 //
658
659 for (int y = tileRange.min.y; y <= tileRange.max.y; ++y)
660 {
661 //
662 // Iterate over all image channels.
663 //
664
665 for (unsigned int i = 0; i < _ifd->slices.size(); ++i)
666 {
667 TInSliceInfo &slice = *_ifd->slices[i];
668
669 //
670 // These offsets are used to facilitate both
671 // absolute and tile-relative pixel coordinates.
672 //
673
674 int xOffsetForData = (slice.xTileCoords == 0) ? 0 : tileRange.min.x;
675 int yOffsetForData = (slice.yTileCoords == 0) ? 0 : tileRange.min.y;
676 int xOffsetForSampleCount =
677 (_ifd->sampleCountXTileCoords == 0) ? 0 : tileRange.min.x;
678 int yOffsetForSampleCount =
679 (_ifd->sampleCountYTileCoords == 0) ? 0 : tileRange.min.y;
680
681 //
682 // Fill the frame buffer with pixel data.
683 //
684
685 if (slice.skip)
686 {
687 //
688 // The file contains data for this channel, but
689 // the frame buffer contains no slice for this channel.
690 //
691
692 skipChannel (readPtr, slice.typeInFile,
693 numPixelsPerScanLine[y - tileRange.min.y]);
694 }
695 else
696 {
697 //
698 // The frame buffer contains a slice for this channel.
699 //
700
701 copyIntoDeepFrameBuffer (readPtr, slice.pointerArrayBase,
702 _ifd->sampleCountSliceBase,
703 _ifd->sampleCountXStride,
704 _ifd->sampleCountYStride,
705 y,
706 tileRange.min.x,
707 tileRange.max.x,
708 xOffsetForSampleCount, yOffsetForSampleCount,
709 xOffsetForData, yOffsetForData,
710 slice.sampleStride,
711 slice.xStride,
712 slice.yStride,
713 slice.fill,
714 slice.fillValue, _tileBuffer->format,
715 slice.typeInFrameBuffer,
716 slice.typeInFile);
717 }
718 }
719 }
720 }
721 catch (std::exception &e)
722 {
723 if (!_tileBuffer->hasException)
724 {
725 _tileBuffer->exception = e.what ();
726 _tileBuffer->hasException = true;
727 }
728 }
729 catch (...)
730 {
731 if (!_tileBuffer->hasException)
732 {
733 _tileBuffer->exception = "unrecognized exception";
734 _tileBuffer->hasException = true;
735 }
736 }
737 }
738
739
740 TileBufferTask *
newTileBufferTask(TaskGroup * group,DeepTiledInputFile::Data * ifd,int number,int dx,int dy,int lx,int ly)741 newTileBufferTask
742 (TaskGroup *group,
743 DeepTiledInputFile::Data *ifd,
744 int number,
745 int dx, int dy,
746 int lx, int ly)
747 {
748 //
749 // Wait for a tile buffer to become available,
750 // fill the buffer with raw data from the file,
751 // and create a new TileBufferTask whose execute()
752 // method will uncompress the tile and copy the
753 // tile's pixels into the frame buffer.
754 //
755
756 TileBuffer *tileBuffer = ifd->getTileBuffer (number);
757
758 try
759 {
760 tileBuffer->wait();
761
762 tileBuffer->dx = dx;
763 tileBuffer->dy = dy;
764 tileBuffer->lx = lx;
765 tileBuffer->ly = ly;
766
767 tileBuffer->uncompressedData = 0;
768
769 readTileData (ifd->_streamData, ifd, dx, dy, lx, ly,
770 tileBuffer->buffer,
771 tileBuffer->dataSize,
772 tileBuffer->uncompressedDataSize);
773 }
774 catch (...)
775 {
776 //
777 // Reading from the file caused an exception.
778 // Signal that the tile buffer is free, and
779 // re-throw the exception.
780 //
781
782 tileBuffer->post();
783 throw;
784 }
785
786 return new TileBufferTask (group, ifd, tileBuffer);
787 }
788
789
790 } // namespace
791
792
DeepTiledInputFile(const char fileName[],int numThreads)793 DeepTiledInputFile::DeepTiledInputFile (const char fileName[], int numThreads):
794 _data (new Data (numThreads))
795 {
796 _data->_deleteStream=true;
797 //
798 // This constructor is called when a user
799 // explicitly wants to read a tiled file.
800 //
801
802 IStream* is = 0;
803 try
804 {
805 is = new StdIFStream (fileName);
806 readMagicNumberAndVersionField(*is, _data->version);
807
808 //
809 // Compatibility to read multpart file.
810 //
811 if (isMultiPart(_data->version))
812 {
813 compatibilityInitialize(*is);
814 }
815 else
816 {
817 _data->_streamData = new InputStreamMutex();
818 _data->_streamData->is = is;
819 _data->header.readFrom (*_data->_streamData->is, _data->version);
820 initialize();
821 _data->tileOffsets.readFrom (*(_data->_streamData->is), _data->fileIsComplete,false,true);
822 _data->_streamData->currentPosition = _data->_streamData->is->tellg();
823 }
824 }
825 catch (IEX_NAMESPACE::BaseExc &e)
826 {
827 if (is) delete is;
828 if (_data && !_data->multiPartBackwardSupport && _data->_streamData) delete _data->_streamData;
829 if (_data) delete _data;
830
831 REPLACE_EXC (e, "Cannot open image file "
832 "\"" << fileName << "\". " << e);
833 throw;
834 }
835 catch (...)
836 {
837 if (is) delete is;
838 if (_data && !_data->multiPartBackwardSupport && _data->_streamData) delete _data->_streamData;
839 if (_data) delete _data;
840
841 throw;
842 }
843 }
844
845
DeepTiledInputFile(OPENEXR_IMF_INTERNAL_NAMESPACE::IStream & is,int numThreads)846 DeepTiledInputFile::DeepTiledInputFile (OPENEXR_IMF_INTERNAL_NAMESPACE::IStream &is, int numThreads):
847 _data (new Data (numThreads))
848 {
849 _data->_streamData=0;
850 _data->_deleteStream=false;
851
852 //
853 // This constructor is called when a user
854 // explicitly wants to read a tiled file.
855 //
856
857 try
858 {
859 readMagicNumberAndVersionField(is, _data->version);
860
861 //
862 // Backward compatibility to read multpart file.
863 //
864 if (isMultiPart(_data->version))
865 {
866 compatibilityInitialize(is);
867 }
868 else
869 {
870 _data->_streamData = new InputStreamMutex();
871 _data->_streamData->is = &is;
872 _data->header.readFrom (*_data->_streamData->is, _data->version);
873 initialize();
874 // file is guaranteed not to be multipart, but is deep
875 _data->tileOffsets.readFrom (*(_data->_streamData->is), _data->fileIsComplete, false,true);
876 _data->memoryMapped = _data->_streamData->is->isMemoryMapped();
877 _data->_streamData->currentPosition = _data->_streamData->is->tellg();
878 }
879 }
880 catch (IEX_NAMESPACE::BaseExc &e)
881 {
882 if (_data && !_data->multiPartBackwardSupport && _data->_streamData) delete _data->_streamData;
883 if (_data) delete _data;
884
885 REPLACE_EXC (e, "Cannot open image file "
886 "\"" << is.fileName() << "\". " << e);
887 throw;
888 }
889 catch (...)
890 {
891 if (_data && !_data->multiPartBackwardSupport && _data->_streamData) delete _data->_streamData;
892 if (_data) delete _data;
893
894 throw;
895 }
896 }
897
898
DeepTiledInputFile(const Header & header,OPENEXR_IMF_INTERNAL_NAMESPACE::IStream * is,int version,int numThreads)899 DeepTiledInputFile::DeepTiledInputFile (const Header &header,
900 OPENEXR_IMF_INTERNAL_NAMESPACE::IStream *is,
901 int version,
902 int numThreads) :
903 _data (new Data (numThreads))
904
905 {
906 _data->_streamData->is = is;
907 _data->_deleteStream=false;
908
909 //
910 // This constructor called by class Imf::InputFile
911 // when a user wants to just read an image file, and
912 // doesn't care or know if the file is tiled.
913 // No need to have backward compatibility here, because
914 // we have the header.
915 //
916
917 _data->header = header;
918 _data->version = version;
919 initialize();
920 _data->tileOffsets.readFrom (*(_data->_streamData->is), _data->fileIsComplete,false,true);
921 _data->memoryMapped = is->isMemoryMapped();
922 _data->_streamData->currentPosition = _data->_streamData->is->tellg();
923 }
924
925
DeepTiledInputFile(InputPartData * part)926 DeepTiledInputFile::DeepTiledInputFile (InputPartData* part) :
927 _data (new Data (part->numThreads))
928 {
929 _data->_deleteStream=false;
930 multiPartInitialize(part);
931 }
932
933
934 void
compatibilityInitialize(OPENEXR_IMF_INTERNAL_NAMESPACE::IStream & is)935 DeepTiledInputFile::compatibilityInitialize(OPENEXR_IMF_INTERNAL_NAMESPACE::IStream& is)
936 {
937 is.seekg(0);
938 //
939 // Construct a MultiPartInputFile, initialize TiledInputFile
940 // with the part 0 data.
941 // (TODO) maybe change the third parameter of the constructor of MultiPartInputFile later.
942 //
943 _data->multiPartFile = new MultiPartInputFile(is, _data->numThreads);
944 _data->multiPartBackwardSupport = true;
945 InputPartData* part = _data->multiPartFile->getPart(0);
946
947 multiPartInitialize(part);
948 }
949
950
951 void
multiPartInitialize(InputPartData * part)952 DeepTiledInputFile::multiPartInitialize(InputPartData* part)
953 {
954 if (isTiled(part->header.type()) == false)
955 THROW (IEX_NAMESPACE::ArgExc, "Can't build a DeepTiledInputFile from a part of type " << part->header.type());
956
957 _data->_streamData = part->mutex;
958 _data->header = part->header;
959 _data->version = part->version;
960 _data->partNumber = part->partNumber;
961 _data->memoryMapped = _data->_streamData->is->isMemoryMapped();
962 initialize();
963 _data->tileOffsets.readFrom(part->chunkOffsets , _data->fileIsComplete);
964 _data->_streamData->currentPosition = _data->_streamData->is->tellg();
965 }
966
967
968 void
initialize()969 DeepTiledInputFile::initialize ()
970 {
971 if (_data->partNumber == -1)
972 if (_data->header.type() != DEEPTILE)
973 throw IEX_NAMESPACE::ArgExc ("Expected a deep tiled file but the file is not deep tiled.");
974 if(_data->header.version()!=1)
975 {
976 THROW(IEX_NAMESPACE::ArgExc, "Version " << _data->header.version() << " not supported for deeptiled images in this version of the library");
977 }
978
979 _data->header.sanityCheck (true);
980
981 _data->tileDesc = _data->header.tileDescription();
982 _data->lineOrder = _data->header.lineOrder();
983
984 //
985 // Save the dataWindow information
986 //
987
988 const Box2i &dataWindow = _data->header.dataWindow();
989 _data->minX = dataWindow.min.x;
990 _data->maxX = dataWindow.max.x;
991 _data->minY = dataWindow.min.y;
992 _data->maxY = dataWindow.max.y;
993
994 //
995 // Precompute level and tile information to speed up utility functions
996 //
997
998 precalculateTileInfo (_data->tileDesc,
999 _data->minX, _data->maxX,
1000 _data->minY, _data->maxY,
1001 _data->numXTiles, _data->numYTiles,
1002 _data->numXLevels, _data->numYLevels);
1003
1004 //
1005 // Create all the TileBuffers and allocate their internal buffers
1006 //
1007
1008 _data->tileOffsets = TileOffsets (_data->tileDesc.mode,
1009 _data->numXLevels,
1010 _data->numYLevels,
1011 _data->numXTiles,
1012 _data->numYTiles);
1013
1014 for (size_t i = 0; i < _data->tileBuffers.size(); i++)
1015 _data->tileBuffers[i] = new TileBuffer ();
1016
1017 _data->maxSampleCountTableSize = _data->tileDesc.ySize *
1018 _data->tileDesc.xSize *
1019 sizeof(int);
1020
1021 _data->sampleCountTableBuffer.resizeErase(_data->maxSampleCountTableSize);
1022
1023 _data->sampleCountTableComp = newCompressor(_data->header.compression(),
1024 _data->maxSampleCountTableSize,
1025 _data->header);
1026
1027
1028 const ChannelList & c=_data->header.channels();
1029 _data->combinedSampleSize=0;
1030 for(ChannelList::ConstIterator i=c.begin();i!=c.end();i++)
1031 {
1032 switch( i.channel().type )
1033 {
1034 case OPENEXR_IMF_INTERNAL_NAMESPACE::HALF :
1035 _data->combinedSampleSize+=Xdr::size<half>();
1036 break;
1037 case OPENEXR_IMF_INTERNAL_NAMESPACE::FLOAT :
1038 _data->combinedSampleSize+=Xdr::size<float>();
1039 break;
1040 case OPENEXR_IMF_INTERNAL_NAMESPACE::UINT :
1041 _data->combinedSampleSize+=Xdr::size<unsigned int>();
1042 break;
1043 default :
1044 THROW(IEX_NAMESPACE::ArgExc, "Bad type for channel " << i.name() << " initializing deepscanline reader");
1045 }
1046 }
1047
1048 }
1049
1050
~DeepTiledInputFile()1051 DeepTiledInputFile::~DeepTiledInputFile ()
1052 {
1053 if (!_data->memoryMapped)
1054 for (size_t i = 0; i < _data->tileBuffers.size(); i++)
1055 if (_data->tileBuffers[i]->buffer != 0)
1056 delete [] _data->tileBuffers[i]->buffer;
1057
1058 if (_data->_deleteStream)
1059 delete _data->_streamData->is;
1060
1061 //
1062 // (TODO) we should have a way to tell if the stream data is owned by this file or
1063 // by a parent multipart file.
1064 //
1065
1066 if (_data->partNumber == -1)
1067 delete _data->_streamData;
1068
1069 delete _data;
1070 }
1071
1072
1073 const char *
fileName() const1074 DeepTiledInputFile::fileName () const
1075 {
1076 return _data->_streamData->is->fileName();
1077 }
1078
1079
1080 const Header &
header() const1081 DeepTiledInputFile::header () const
1082 {
1083 return _data->header;
1084 }
1085
1086
1087 int
version() const1088 DeepTiledInputFile::version () const
1089 {
1090 return _data->version;
1091 }
1092
1093
1094 void
setFrameBuffer(const DeepFrameBuffer & frameBuffer)1095 DeepTiledInputFile::setFrameBuffer (const DeepFrameBuffer &frameBuffer)
1096 {
1097 Lock lock (*_data->_streamData);
1098
1099 //
1100 // Set the frame buffer
1101 //
1102
1103 //
1104 // Check if the new frame buffer descriptor is
1105 // compatible with the image file header.
1106 //
1107
1108 const ChannelList &channels = _data->header.channels();
1109
1110 for (DeepFrameBuffer::ConstIterator j = frameBuffer.begin();
1111 j != frameBuffer.end();
1112 ++j)
1113 {
1114 ChannelList::ConstIterator i = channels.find (j.name());
1115
1116 if (i == channels.end())
1117 continue;
1118
1119 if (i.channel().xSampling != j.slice().xSampling ||
1120 i.channel().ySampling != j.slice().ySampling)
1121 THROW (IEX_NAMESPACE::ArgExc, "X and/or y subsampling factors "
1122 "of \"" << i.name() << "\" channel "
1123 "of input file \"" << fileName() << "\" are "
1124 "not compatible with the frame buffer's "
1125 "subsampling factors.");
1126 }
1127
1128 //
1129 // Store the pixel sample count table.
1130 // (TODO) Support for different sampling rates?
1131 //
1132
1133 const Slice& sampleCountSlice = frameBuffer.getSampleCountSlice();
1134 if (sampleCountSlice.base == 0)
1135 {
1136 throw IEX_NAMESPACE::ArgExc ("Invalid base pointer, please set a proper sample count slice.");
1137 }
1138 else
1139 {
1140 _data->sampleCountSliceBase = sampleCountSlice.base;
1141 _data->sampleCountXStride = sampleCountSlice.xStride;
1142 _data->sampleCountYStride = sampleCountSlice.yStride;
1143 _data->sampleCountXTileCoords = sampleCountSlice.xTileCoords;
1144 _data->sampleCountYTileCoords = sampleCountSlice.yTileCoords;
1145 }
1146
1147 //
1148 // Initialize the slice table for readPixels().
1149 //
1150
1151 vector<TInSliceInfo*> slices;
1152 ChannelList::ConstIterator i = channels.begin();
1153
1154 for (DeepFrameBuffer::ConstIterator j = frameBuffer.begin();
1155 j != frameBuffer.end();
1156 ++j)
1157 {
1158 while (i != channels.end() && strcmp (i.name(), j.name()) < 0)
1159 {
1160 //
1161 // Channel i is present in the file but not
1162 // in the frame buffer; data for channel i
1163 // will be skipped during readPixels().
1164 //
1165
1166 slices.push_back (new TInSliceInfo (i.channel().type,
1167 NULL,
1168 i.channel().type,
1169 0, // xStride
1170 0, // yStride
1171 0, // sampleStride
1172 false, // fill
1173 true, // skip
1174 0.0)); // fillValue
1175 ++i;
1176 }
1177
1178 bool fill = false;
1179
1180 if (i == channels.end() || strcmp (i.name(), j.name()) > 0)
1181 {
1182 //
1183 // Channel i is present in the frame buffer, but not in the file.
1184 // In the frame buffer, slice j will be filled with a default value.
1185 //
1186
1187 fill = true;
1188 }
1189
1190 slices.push_back (new TInSliceInfo (j.slice().type,
1191 j.slice().base,
1192 fill? j.slice().type: i.channel().type,
1193 j.slice().xStride,
1194 j.slice().yStride,
1195 j.slice().sampleStride,
1196 fill,
1197 false, // skip
1198 j.slice().fillValue,
1199 (j.slice().xTileCoords)? 1: 0,
1200 (j.slice().yTileCoords)? 1: 0));
1201
1202
1203 if (i != channels.end() && !fill)
1204 ++i;
1205 }
1206
1207 // (TODO) inspect the following code. It's additional to the scanline input file.
1208 // Is this needed?
1209
1210 while (i != channels.end())
1211 {
1212 //
1213 // Channel i is present in the file but not
1214 // in the frame buffer; data for channel i
1215 // will be skipped during readPixels().
1216 //
1217
1218 slices.push_back (new TInSliceInfo (i.channel().type,
1219 NULL,
1220 i.channel().type,
1221 0, // xStride
1222 0, // yStride
1223 0, // sampleStride
1224 false, // fill
1225 true, // skip
1226 0.0)); // fillValue
1227 ++i;
1228 }
1229
1230 //
1231 // Store the new frame buffer.
1232 //
1233
1234 _data->frameBuffer = frameBuffer;
1235
1236 for (size_t i = 0; i < _data->slices.size(); i++)
1237 delete _data->slices[i];
1238 _data->slices = slices;
1239 }
1240
1241
1242 const DeepFrameBuffer &
frameBuffer() const1243 DeepTiledInputFile::frameBuffer () const
1244 {
1245 Lock lock (*_data->_streamData);
1246 return _data->frameBuffer;
1247 }
1248
1249
1250 bool
isComplete() const1251 DeepTiledInputFile::isComplete () const
1252 {
1253 return _data->fileIsComplete;
1254 }
1255
1256
1257 void
readTiles(int dx1,int dx2,int dy1,int dy2,int lx,int ly)1258 DeepTiledInputFile::readTiles (int dx1, int dx2, int dy1, int dy2, int lx, int ly)
1259 {
1260 //
1261 // Read a range of tiles from the file into the framebuffer
1262 //
1263
1264 try
1265 {
1266 Lock lock (*_data->_streamData);
1267
1268 if (_data->slices.size() == 0)
1269 throw IEX_NAMESPACE::ArgExc ("No frame buffer specified "
1270 "as pixel data destination.");
1271
1272 if (!isValidLevel (lx, ly))
1273 THROW (IEX_NAMESPACE::ArgExc,
1274 "Level coordinate "
1275 "(" << lx << ", " << ly << ") "
1276 "is invalid.");
1277
1278 //
1279 // Determine the first and last tile coordinates in both dimensions.
1280 // We always attempt to read the range of tiles in the order that
1281 // they are stored in the file.
1282 //
1283
1284 if (dx1 > dx2)
1285 std::swap (dx1, dx2);
1286
1287 if (dy1 > dy2)
1288 std::swap (dy1, dy2);
1289
1290 int dyStart = dy1;
1291 int dyStop = dy2 + 1;
1292 int dY = 1;
1293
1294 if (_data->lineOrder == DECREASING_Y)
1295 {
1296 dyStart = dy2;
1297 dyStop = dy1 - 1;
1298 dY = -1;
1299 }
1300
1301 //
1302 // Create a task group for all tile buffer tasks. When the
1303 // task group goes out of scope, the destructor waits until
1304 // all tasks are complete.
1305 //
1306
1307 {
1308 TaskGroup taskGroup;
1309 int tileNumber = 0;
1310
1311 for (int dy = dyStart; dy != dyStop; dy += dY)
1312 {
1313 for (int dx = dx1; dx <= dx2; dx++)
1314 {
1315 if (!isValidTile (dx, dy, lx, ly))
1316 THROW (IEX_NAMESPACE::ArgExc,
1317 "Tile (" << dx << ", " << dy << ", " <<
1318 lx << "," << ly << ") is not a valid tile.");
1319
1320 ThreadPool::addGlobalTask (newTileBufferTask (&taskGroup,
1321 _data,
1322 tileNumber++,
1323 dx, dy,
1324 lx, ly));
1325 }
1326 }
1327
1328 //
1329 // finish all tasks
1330 //
1331 }
1332
1333 //
1334 // Exeption handling:
1335 //
1336 // TileBufferTask::execute() may have encountered exceptions, but
1337 // those exceptions occurred in another thread, not in the thread
1338 // that is executing this call to TiledInputFile::readTiles().
1339 // TileBufferTask::execute() has caught all exceptions and stored
1340 // the exceptions' what() strings in the tile buffers.
1341 // Now we check if any tile buffer contains a stored exception; if
1342 // this is the case then we re-throw the exception in this thread.
1343 // (It is possible that multiple tile buffers contain stored
1344 // exceptions. We re-throw the first exception we find and
1345 // ignore all others.)
1346 //
1347
1348 const string *exception = 0;
1349
1350 for (size_t i = 0; i < _data->tileBuffers.size(); ++i)
1351 {
1352 TileBuffer *tileBuffer = _data->tileBuffers[i];
1353
1354 if (tileBuffer->hasException && !exception)
1355 exception = &tileBuffer->exception;
1356
1357 tileBuffer->hasException = false;
1358 }
1359
1360 if (exception)
1361 throw IEX_NAMESPACE::IoExc (*exception);
1362 }
1363 catch (IEX_NAMESPACE::BaseExc &e)
1364 {
1365 REPLACE_EXC (e, "Error reading pixel data from image "
1366 "file \"" << fileName() << "\". " << e);
1367 throw;
1368 }
1369 }
1370
1371
1372 void
readTiles(int dx1,int dx2,int dy1,int dy2,int l)1373 DeepTiledInputFile::readTiles (int dx1, int dx2, int dy1, int dy2, int l)
1374 {
1375 readTiles (dx1, dx2, dy1, dy2, l, l);
1376 }
1377
1378
1379 void
readTile(int dx,int dy,int lx,int ly)1380 DeepTiledInputFile::readTile (int dx, int dy, int lx, int ly)
1381 {
1382 readTiles (dx, dx, dy, dy, lx, ly);
1383 }
1384
1385
1386 void
readTile(int dx,int dy,int l)1387 DeepTiledInputFile::readTile (int dx, int dy, int l)
1388 {
1389 readTile (dx, dy, l, l);
1390 }
1391
1392
1393 void
rawTileData(int & dx,int & dy,int & lx,int & ly,char * pixelData,Int64 & pixelDataSize) const1394 DeepTiledInputFile::rawTileData (int &dx, int &dy,
1395 int &lx, int &ly,
1396 char * pixelData,
1397 Int64 &pixelDataSize) const
1398 {
1399 if (!isValidTile (dx, dy, lx, ly))
1400 throw IEX_NAMESPACE::ArgExc ("Tried to read a tile outside "
1401 "the image file's data window.");
1402
1403 Int64 tileOffset = _data->tileOffsets (dx, dy, lx, ly);
1404
1405 if(tileOffset == 0)
1406 {
1407 THROW (IEX_NAMESPACE::InputExc, "Tile (" << dx << ", " << dy << ", " <<
1408 lx << ", " << ly << ") is missing.");
1409 }
1410
1411 Lock lock(*_data->_streamData);
1412
1413 if (_data->_streamData->is->tellg() != tileOffset)
1414 _data->_streamData->is->seekg (tileOffset);
1415
1416
1417 //
1418 // Read the first few bytes of the tile (the header).
1419 // Verify that the tile coordinates and the level number
1420 // are correct.
1421 //
1422
1423 int tileXCoord, tileYCoord, levelX, levelY;
1424
1425 if (isMultiPart(_data->version))
1426 {
1427 int partNumber;
1428 Xdr::read <StreamIO> (*_data->_streamData->is, partNumber);
1429 if (partNumber != _data->partNumber)
1430 {
1431 THROW (IEX_NAMESPACE::ArgExc, "Unexpected part number " << partNumber
1432 << ", should be " << _data->partNumber << ".");
1433 }
1434 }
1435
1436 Xdr::read <StreamIO> (*_data->_streamData->is, tileXCoord);
1437 Xdr::read <StreamIO> (*_data->_streamData->is, tileYCoord);
1438 Xdr::read <StreamIO> (*_data->_streamData->is, levelX);
1439 Xdr::read <StreamIO> (*_data->_streamData->is, levelY);
1440
1441 Int64 sampleCountTableSize;
1442 Int64 packedDataSize;
1443 Xdr::read <StreamIO> (*_data->_streamData->is, sampleCountTableSize);
1444
1445 Xdr::read <StreamIO> (*_data->_streamData->is, packedDataSize);
1446
1447
1448
1449 if (tileXCoord != dx)
1450 throw IEX_NAMESPACE::InputExc ("Unexpected tile x coordinate.");
1451
1452 if (tileYCoord != dy)
1453 throw IEX_NAMESPACE::InputExc ("Unexpected tile y coordinate.");
1454
1455 if (levelX != lx)
1456 throw IEX_NAMESPACE::InputExc ("Unexpected tile x level number coordinate.");
1457
1458 if (levelY != ly)
1459 throw IEX_NAMESPACE::InputExc ("Unexpected tile y level number coordinate.");
1460
1461
1462 // total requirement for reading all the data
1463
1464 Int64 totalSizeRequired=40+sampleCountTableSize+packedDataSize;
1465
1466 bool big_enough = totalSizeRequired<=pixelDataSize;
1467
1468 pixelDataSize = totalSizeRequired;
1469
1470 // was the block we were given big enough?
1471 if(!big_enough || pixelData==NULL)
1472 {
1473 // special case: seek stream back to start if we are at the beginning (regular reading pixels assumes it doesn't need to seek
1474 // in single part files)
1475 if(!isMultiPart(_data->version))
1476 {
1477 _data->_streamData->is->seekg(_data->_streamData->currentPosition);
1478 }
1479 // leave lock here - bail before reading more data
1480 return;
1481 }
1482
1483 // copy the values we have read into the output block
1484 *(int *) (pixelData+0) = dx;
1485 *(int *) (pixelData+4) = dy;
1486 *(int *) (pixelData+8) = levelX;
1487 *(int *) (pixelData+12) = levelY;
1488 *(Int64 *) (pixelData+16) =sampleCountTableSize;
1489 *(Int64 *) (pixelData+24) = packedDataSize;
1490
1491 // didn't read the unpackedsize - do that now
1492 Xdr::read<StreamIO> (*_data->_streamData->is, *(Int64 *) (pixelData+32));
1493
1494 // read the actual data
1495 _data->_streamData->is->read(pixelData+40, sampleCountTableSize+packedDataSize);
1496
1497
1498 if(!isMultiPart(_data->version))
1499 {
1500 _data->_streamData->currentPosition+=sampleCountTableSize+packedDataSize+40;
1501 }
1502
1503 // leave lock here
1504
1505
1506 }
1507
1508
1509 unsigned int
tileXSize() const1510 DeepTiledInputFile::tileXSize () const
1511 {
1512 return _data->tileDesc.xSize;
1513 }
1514
1515
1516 unsigned int
tileYSize() const1517 DeepTiledInputFile::tileYSize () const
1518 {
1519 return _data->tileDesc.ySize;
1520 }
1521
1522
1523 LevelMode
levelMode() const1524 DeepTiledInputFile::levelMode () const
1525 {
1526 return _data->tileDesc.mode;
1527 }
1528
1529
1530 LevelRoundingMode
levelRoundingMode() const1531 DeepTiledInputFile::levelRoundingMode () const
1532 {
1533 return _data->tileDesc.roundingMode;
1534 }
1535
1536
1537 int
numLevels() const1538 DeepTiledInputFile::numLevels () const
1539 {
1540 if (levelMode() == RIPMAP_LEVELS)
1541 THROW (IEX_NAMESPACE::LogicExc, "Error calling numLevels() on image "
1542 "file \"" << fileName() << "\" "
1543 "(numLevels() is not defined for files "
1544 "with RIPMAP level mode).");
1545
1546 return _data->numXLevels;
1547 }
1548
1549
1550 int
numXLevels() const1551 DeepTiledInputFile::numXLevels () const
1552 {
1553 return _data->numXLevels;
1554 }
1555
1556
1557 int
numYLevels() const1558 DeepTiledInputFile::numYLevels () const
1559 {
1560 return _data->numYLevels;
1561 }
1562
1563
1564 bool
isValidLevel(int lx,int ly) const1565 DeepTiledInputFile::isValidLevel (int lx, int ly) const
1566 {
1567 if (lx < 0 || ly < 0)
1568 return false;
1569
1570 if (levelMode() == MIPMAP_LEVELS && lx != ly)
1571 return false;
1572
1573 if (lx >= numXLevels() || ly >= numYLevels())
1574 return false;
1575
1576 return true;
1577 }
1578
1579
1580 int
levelWidth(int lx) const1581 DeepTiledInputFile::levelWidth (int lx) const
1582 {
1583 try
1584 {
1585 return levelSize (_data->minX, _data->maxX, lx,
1586 _data->tileDesc.roundingMode);
1587 }
1588 catch (IEX_NAMESPACE::BaseExc &e)
1589 {
1590 REPLACE_EXC (e, "Error calling levelWidth() on image "
1591 "file \"" << fileName() << "\". " << e);
1592 throw;
1593 }
1594 }
1595
1596
1597 int
levelHeight(int ly) const1598 DeepTiledInputFile::levelHeight (int ly) const
1599 {
1600 try
1601 {
1602 return levelSize (_data->minY, _data->maxY, ly,
1603 _data->tileDesc.roundingMode);
1604 }
1605 catch (IEX_NAMESPACE::BaseExc &e)
1606 {
1607 REPLACE_EXC (e, "Error calling levelHeight() on image "
1608 "file \"" << fileName() << "\". " << e);
1609 throw;
1610 }
1611 }
1612
1613
1614 int
numXTiles(int lx) const1615 DeepTiledInputFile::numXTiles (int lx) const
1616 {
1617 if (lx < 0 || lx >= _data->numXLevels)
1618 {
1619 THROW (IEX_NAMESPACE::ArgExc, "Error calling numXTiles() on image "
1620 "file \"" << _data->_streamData->is->fileName() << "\" "
1621 "(Argument is not in valid range).");
1622
1623 }
1624
1625 return _data->numXTiles[lx];
1626 }
1627
1628
1629 int
numYTiles(int ly) const1630 DeepTiledInputFile::numYTiles (int ly) const
1631 {
1632 if (ly < 0 || ly >= _data->numYLevels)
1633 {
1634 THROW (IEX_NAMESPACE::ArgExc, "Error calling numYTiles() on image "
1635 "file \"" << _data->_streamData->is->fileName() << "\" "
1636 "(Argument is not in valid range).");
1637 }
1638
1639 return _data->numYTiles[ly];
1640 }
1641
1642
1643 Box2i
dataWindowForLevel(int l) const1644 DeepTiledInputFile::dataWindowForLevel (int l) const
1645 {
1646 return dataWindowForLevel (l, l);
1647 }
1648
1649
1650 Box2i
dataWindowForLevel(int lx,int ly) const1651 DeepTiledInputFile::dataWindowForLevel (int lx, int ly) const
1652 {
1653 try
1654 {
1655 return OPENEXR_IMF_INTERNAL_NAMESPACE::dataWindowForLevel (
1656 _data->tileDesc,
1657 _data->minX, _data->maxX,
1658 _data->minY, _data->maxY,
1659 lx, ly);
1660 }
1661 catch (IEX_NAMESPACE::BaseExc &e)
1662 {
1663 REPLACE_EXC (e, "Error calling dataWindowForLevel() on image "
1664 "file \"" << fileName() << "\". " << e);
1665 throw;
1666 }
1667 }
1668
1669
1670 Box2i
dataWindowForTile(int dx,int dy,int l) const1671 DeepTiledInputFile::dataWindowForTile (int dx, int dy, int l) const
1672 {
1673 return dataWindowForTile (dx, dy, l, l);
1674 }
1675
1676
1677 Box2i
dataWindowForTile(int dx,int dy,int lx,int ly) const1678 DeepTiledInputFile::dataWindowForTile (int dx, int dy, int lx, int ly) const
1679 {
1680 try
1681 {
1682 if (!isValidTile (dx, dy, lx, ly))
1683 throw IEX_NAMESPACE::ArgExc ("Arguments not in valid range.");
1684
1685 return OPENEXR_IMF_INTERNAL_NAMESPACE::dataWindowForTile (
1686 _data->tileDesc,
1687 _data->minX, _data->maxX,
1688 _data->minY, _data->maxY,
1689 dx, dy, lx, ly);
1690 }
1691 catch (IEX_NAMESPACE::BaseExc &e)
1692 {
1693 REPLACE_EXC (e, "Error calling dataWindowForTile() on image "
1694 "file \"" << fileName() << "\". " << e);
1695 throw;
1696 }
1697 }
1698
1699
1700 bool
isValidTile(int dx,int dy,int lx,int ly) const1701 DeepTiledInputFile::isValidTile (int dx, int dy, int lx, int ly) const
1702 {
1703 return ((lx < _data->numXLevels && lx >= 0) &&
1704 (ly < _data->numYLevels && ly >= 0) &&
1705 (dx < _data->numXTiles[lx] && dx >= 0) &&
1706 (dy < _data->numYTiles[ly] && dy >= 0));
1707 }
1708
1709
1710 void
readPixelSampleCounts(int dx1,int dx2,int dy1,int dy2,int lx,int ly)1711 DeepTiledInputFile::readPixelSampleCounts (int dx1, int dx2,
1712 int dy1, int dy2,
1713 int lx, int ly)
1714 {
1715 Int64 savedFilePos = 0;
1716
1717 try
1718 {
1719 Lock lock (*_data->_streamData);
1720
1721 savedFilePos = _data->_streamData->is->tellg();
1722
1723
1724 if (!isValidLevel (lx, ly))
1725 {
1726 THROW (IEX_NAMESPACE::ArgExc,
1727 "Level coordinate "
1728 "(" << lx << ", " << ly << ") "
1729 "is invalid.");
1730 }
1731
1732 if (dx1 > dx2)
1733 std::swap (dx1, dx2);
1734
1735 if (dy1 > dy2)
1736 std::swap (dy1, dy2);
1737
1738 int dyStart = dy1;
1739 int dyStop = dy2 + 1;
1740 int dY = 1;
1741
1742 if (_data->lineOrder == DECREASING_Y)
1743 {
1744 dyStart = dy2;
1745 dyStop = dy1 - 1;
1746 dY = -1;
1747 }
1748
1749 // (TODO) Check if we have read the sample counts for those tiles,
1750 // if we have, no need to read again.
1751 for (int dy = dyStart; dy != dyStop; dy += dY)
1752 {
1753 for (int dx = dx1; dx <= dx2; dx++)
1754 {
1755
1756 if (!isValidTile (dx, dy, lx, ly))
1757 {
1758 THROW (IEX_NAMESPACE::ArgExc,
1759 "Tile (" << dx << ", " << dy << ", " <<
1760 lx << "," << ly << ") is not a valid tile.");
1761 }
1762
1763 Box2i tileRange = OPENEXR_IMF_INTERNAL_NAMESPACE::dataWindowForTile (
1764 _data->tileDesc,
1765 _data->minX, _data->maxX,
1766 _data->minY, _data->maxY,
1767 dx, dy, lx, ly);
1768
1769 int xOffset = _data->sampleCountXTileCoords * tileRange.min.x;
1770 int yOffset = _data->sampleCountYTileCoords * tileRange.min.y;
1771
1772 //
1773 // Skip and check the tile coordinates.
1774 //
1775
1776 _data->_streamData->is->seekg(_data->tileOffsets(dx, dy, lx, ly));
1777
1778 if (isMultiPart(_data->version))
1779 {
1780 int partNumber;
1781 Xdr::read <StreamIO> (*_data->_streamData->is, partNumber);
1782
1783 if (partNumber != _data->partNumber)
1784 throw IEX_NAMESPACE::InputExc ("Unexpected part number.");
1785 }
1786
1787 int xInFile, yInFile, lxInFile, lyInFile;
1788 Xdr::read <StreamIO> (*_data->_streamData->is, xInFile);
1789 Xdr::read <StreamIO> (*_data->_streamData->is, yInFile);
1790 Xdr::read <StreamIO> (*_data->_streamData->is, lxInFile);
1791 Xdr::read <StreamIO> (*_data->_streamData->is, lyInFile);
1792
1793 if (xInFile != dx)
1794 throw IEX_NAMESPACE::InputExc ("Unexpected tile x coordinate.");
1795
1796 if (yInFile != dy)
1797 throw IEX_NAMESPACE::InputExc ("Unexpected tile y coordinate.");
1798
1799 if (lxInFile != lx)
1800 throw IEX_NAMESPACE::InputExc ("Unexpected tile x level number coordinate.");
1801
1802 if (lyInFile != ly)
1803 throw IEX_NAMESPACE::InputExc ("Unexpected tile y level number coordinate.");
1804
1805 Int64 tableSize, dataSize, unpackedDataSize;
1806 Xdr::read <StreamIO> (*_data->_streamData->is, tableSize);
1807 Xdr::read <StreamIO> (*_data->_streamData->is, dataSize);
1808 Xdr::read <StreamIO> (*_data->_streamData->is, unpackedDataSize);
1809
1810
1811 if(tableSize>_data->maxSampleCountTableSize)
1812 {
1813 THROW (IEX_NAMESPACE::ArgExc, "Bad sampleCountTableDataSize read from tile "<< dx << ',' << dy << ',' << lx << ',' << ly << ": expected " << _data->maxSampleCountTableSize << " or less, got "<< tableSize);
1814 }
1815
1816
1817 //
1818 // We make a check on the data size requirements here.
1819 // Whilst we wish to store 64bit sizes on disk, not all the compressors
1820 // have been made to work with such data sizes and are still limited to
1821 // using signed 32 bit (int) for the data size. As such, this version
1822 // insists that we validate that the data size does not exceed the data
1823 // type max limit.
1824 // @TODO refactor the compressor code to ensure full 64-bit support.
1825 //
1826
1827 Int64 compressorMaxDataSize = Int64(std::numeric_limits<int>::max());
1828 if (dataSize > compressorMaxDataSize ||
1829 unpackedDataSize > compressorMaxDataSize ||
1830 tableSize > compressorMaxDataSize)
1831 {
1832 THROW (IEX_NAMESPACE::ArgExc, "This version of the library does not"
1833 << "support the allocation of data with size > "
1834 << compressorMaxDataSize
1835 << " file table size :" << tableSize
1836 << " file unpacked size :" << unpackedDataSize
1837 << " file packed size :" << dataSize << ".\n");
1838 }
1839
1840 //
1841 // Read and uncompress the pixel sample count table.
1842 //
1843
1844 _data->_streamData->is->read(_data->sampleCountTableBuffer, tableSize);
1845
1846 const char* readPtr;
1847
1848 if (tableSize < _data->maxSampleCountTableSize)
1849 {
1850 if(!_data->sampleCountTableComp)
1851 {
1852 THROW(IEX_NAMESPACE::ArgExc,"Deep scanline data corrupt at tile " << dx << ',' << dy << ',' << lx << ',' << ly << " (sampleCountTableDataSize error)");
1853 }
1854 _data->sampleCountTableComp->uncompress(_data->sampleCountTableBuffer,
1855 tableSize,
1856 tileRange.min.y,
1857 readPtr);
1858 }
1859 else
1860 readPtr = _data->sampleCountTableBuffer;
1861
1862 size_t cumulative_total_samples =0;
1863 int lastAccumulatedCount;
1864 for (int j = tileRange.min.y; j <= tileRange.max.y; j++)
1865 {
1866 lastAccumulatedCount = 0;
1867 for (int i = tileRange.min.x; i <= tileRange.max.x; i++)
1868 {
1869 int accumulatedCount;
1870 Xdr::read <CharPtrIO> (readPtr, accumulatedCount);
1871
1872 if (accumulatedCount < lastAccumulatedCount)
1873 {
1874 THROW(IEX_NAMESPACE::ArgExc,"Deep tile sampleCount data corrupt at tile "
1875 << dx << ',' << dy << ',' << lx << ',' << ly << " (negative sample count detected)");
1876 }
1877
1878 int count = accumulatedCount - lastAccumulatedCount;
1879 lastAccumulatedCount = accumulatedCount;
1880
1881 _data->getSampleCount(i - xOffset, j - yOffset) =count;
1882 }
1883 cumulative_total_samples += lastAccumulatedCount;
1884 }
1885
1886 if(cumulative_total_samples * _data->combinedSampleSize > unpackedDataSize)
1887 {
1888 THROW(IEX_NAMESPACE::ArgExc,"Deep scanline sampleCount data corrupt at tile "
1889 << dx << ',' << dy << ',' << lx << ',' << ly
1890 << ": pixel data only contains " << unpackedDataSize
1891 << " bytes of data but table references at least "
1892 << cumulative_total_samples*_data->combinedSampleSize << " bytes of sample data" );
1893 }
1894
1895 }
1896 }
1897
1898 _data->_streamData->is->seekg(savedFilePos);
1899 }
1900 catch (IEX_NAMESPACE::BaseExc &e)
1901 {
1902 REPLACE_EXC (e, "Error reading sample count data from image "
1903 "file \"" << fileName() << "\". " << e);
1904
1905 _data->_streamData->is->seekg(savedFilePos);
1906
1907 throw;
1908 }
1909 }
1910
1911
1912 void
readPixelSampleCount(int dx,int dy,int l)1913 DeepTiledInputFile::readPixelSampleCount (int dx, int dy, int l)
1914 {
1915 readPixelSampleCount (dx, dy, l, l);
1916 }
1917
1918
1919 void
readPixelSampleCount(int dx,int dy,int lx,int ly)1920 DeepTiledInputFile::readPixelSampleCount (int dx, int dy, int lx, int ly)
1921 {
1922 readPixelSampleCounts (dx, dx, dy, dy, lx, ly);
1923 }
1924
1925
1926 void
readPixelSampleCounts(int dx1,int dx2,int dy1,int dy2,int l)1927 DeepTiledInputFile::readPixelSampleCounts (int dx1, int dx2,
1928 int dy1, int dy2,
1929 int l)
1930 {
1931 readPixelSampleCounts (dx1, dx2, dy1, dy2, l, l);
1932 }
1933
1934
1935 size_t
totalTiles() const1936 DeepTiledInputFile::totalTiles() const
1937 {
1938 //
1939 // Calculate the total number of tiles in the file
1940 //
1941
1942 int numAllTiles = 0;
1943
1944 switch (levelMode ())
1945 {
1946 case ONE_LEVEL:
1947 case MIPMAP_LEVELS:
1948
1949 for (int i_l = 0; i_l < numLevels (); ++i_l)
1950 numAllTiles += numXTiles (i_l) * numYTiles (i_l);
1951
1952 break;
1953
1954 case RIPMAP_LEVELS:
1955
1956 for (int i_ly = 0; i_ly < numYLevels (); ++i_ly)
1957 for (int i_lx = 0; i_lx < numXLevels (); ++i_lx)
1958 numAllTiles += numXTiles (i_lx) * numYTiles (i_ly);
1959
1960 break;
1961
1962 default:
1963
1964 throw IEX_NAMESPACE::ArgExc ("Unknown LevelMode format.");
1965 }
1966 return numAllTiles;
1967 }
1968
1969
1970
1971
1972 void
getTileOrder(int dx[],int dy[],int lx[],int ly[]) const1973 DeepTiledInputFile::getTileOrder(int dx[],int dy[],int lx[],int ly[]) const
1974 {
1975 return _data->tileOffsets.getTileOrder(dx,dy,lx,ly);
1976
1977 }
1978
1979 OPENEXR_IMF_INTERNAL_NAMESPACE_SOURCE_EXIT
1980